JustDraw

Project Url: LiuHongtao/JustDraw
Introduction: A Test for Android Canvas Painting with JavaScript Engine
More: Author   ReportBugs   
Tags:

Canvas 绘制测试 Demo,点击下方 ICON 下载 Apk。

ICON

单个图形如下图所示:

Shape

  • 长度为 300 的线
  • 边长为 20 的正方形
  • 直径为 2 的圆

可选择绘制 1000、5000、10000 次,查看不同绘制方式的效果及逻辑执行时间。

UI

绘制逻辑

Native Canvas

View 的onDraw(Canvas canvas)方法中循环执行 Canvas 绘制,单次绘制如下:

canvas.drawLine(x, y, x + 300, y, mPaint);
canvas.drawRect(x, y, x + 20, y + 20, mPaint);
canvas.drawCircle(x, y, 2, mPaint);

Native Canvas with Path

View 的onDraw(Canvas canvas)方法中循环执行 Path 绘制,单次绘制如下:

path = new Path();
path.moveTo(x, y);
path.lineTo(x + 300, y);

path.addRect(x, y, x + 20, y + 20, Path.Direction.CW);

RectF rectF = new RectF(x - 2, y - 2, x + 2, y + 2);
path.addArc(rectF, 0, (float)(360 / Math.PI * 180));

canvas.drawPath(path, mPaint);

Native Canvas Pro

先将图形绘制到 Bitmap 中,再在onDraw(Canvas canvas)方法中绘制该 Bitmap。

Web

在 WebView 中进行 Canvas 绘制,JS 代码中单次绘制如下:

ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(x + 300, y);
ctx.closePath();

ctx.rect(x, y, 20, 20);

ctx.arc(x, y, 2, 0, 360, false);

ctx.stroke();

V8 & Canvas

通过V8J2V8运行 JS 代码,并调用 Native 的 Canvas 进行绘制。JS 绘制代码同上。

Just Canvas

本部分是对 V8&Canvas 的优化,计划开发成轮子。为测试性能,目前仅开发一个 Demo。JS 绘制代码同上。

V8 & Canvas 方案中每执行一个 JS 中的绘制方法,则通过 JNI 调用一次本地 Canvas 方法,而跨语言调用对性能有影响。本方案中通过格式化 JS 的 Java 调用,通过调用一次 Java 方法,传递需要执行的方法和参数组成的字符串,在 Java 端解析字符串并相应调用,以此减少 JNI 调用次数。自测较 V8 & Canvas 方案执行速度提升 50%。

Java 端解析也有数种方案,Demo 中提供了不同的方法供测试。

  • jcdemo:JS 传回整个一个对象数组,对象内容为方法(name)和参数(parameter),每一个变量即是一个 V8Object,但不断的获取 V8Object 是较为耗时的操作,此处待研究
  • jcdemoString:自定义的字符串格式,一个字符串传递所有的方法调用,并自行解析(参考了网上资料,没有使用 String.split,因较为耗时)
  • jcdemoJson:JS 传回 jcdemo 对象的 JSON 字符串,解决了不断获取 V8Object 的问题,但 JSON 解析较为耗时(2s 解析 7w 个方法调用,在 JSON 看来不慢,但在绘制看来还需提高)

Native 绘制性能

通过Native CanvasNative Canvas with Path可发现,Native 绘制中,直接使用canvas.drawXXXpath.addXXX+canvas.drawPath之间性能存在差异,后者较前者慢。原因可参见《Path 绘制和硬件加速》

Paths are always rendered using the CPU. When the app is hardware accelerated this means the renderer will first draw your path using the CPU into a bitmap, then upload that bitmap as a texture to the GPU and finally draw the texture on screen.

Path 往往是通过 CPU 进行绘制。App 启用硬件加速,意味着渲染器会首先将 Path 通过 CPU 绘制到 Bitmap,然后将 Bitmap 作为 Texture 传到 GPU,最后将 Texture 通过 GPU 画到屏幕上。

因此在Native Canvas Pro中先将图形绘制到一个 Bitmap,再通过canvas.drawBitmap绘制,绘制部分时间大大减少。

V8 & Canvas API

通过 JS 引擎运行 JS 代码,调用本地 Canvas 方法实现绘制。 目前仅实现部分 JS 端 Canvas 接口,实现描述如下:

  • 接口参数类型、顺序和数量均以 JavaScript 为准
  • 属性设置由直接赋值转换为方法调用,如ctx.lineWidth = 5转换为ctx.setLineWidth(5),并暂时无法通过ctx.lineWidthctx.getLineWidth()获取
  • 颜色统一为字符串形式色码,如"#188ffc"

Window API

  • createCanvas()
  • devicePixelRatio

Canvas API

  • setSize(x, y, width, height)
  • setX(x)
  • setY(y)
  • setWidth(width)
  • setHeight(height)
  • context

Context API

属性(2)
  • setFillColor(color)
  • setStrokeColor(color)
线条样式(2)
  • setLineWidth(width)
  • setLineDash(intervals)
矩形(2)
  • fillRect(x, y, width, height)
  • strokeRect(x, y, width, height)
路径(10)
  • beginPath()
  • closePath()
  • stroke()
  • fill()
  • moveTo(x, y)
  • lineTo(x, y)
  • quadraticCurveTo(cpx, cpy, x, y)
  • bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)
  • arc(x, y, r, sAngle, eAngle, counterclockwise)
  • rect(x, y, width, height)
转换(4)
  • translate(x, y)
  • transform(a, b, c, d, dx, dy)
  • scale(scaleWidth, scaleHeight)
  • rotate(angle)
文本(3)
  • setFontSize(fontSize)
  • fillText(text, x, y)
  • measureText(text)
合成(1)
  • setGlobalAlpha(alpha)
其他(2)
  • save()
  • restore()
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools