Canvas绘制测试Demo,点击下方ICON下载Apk。
单个图形如下图所示:
- 长度为300的线
- 边长为20的正方形
- 直径为2的圆
可选择绘制1000、5000、10000次,查看不同绘制方式的效果及逻辑执行时间。
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);
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);
先将图形绘制到Bitmap中,再在onDraw(Canvas canvas)
方法中绘制该Bitmap。
在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和J2V8运行JS代码,并调用Native的Canvas进行绘制。JS绘制代码同上。
本部分是对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 Canvas和Native Canvas with Path可发现,Native绘制中,直接使用canvas.drawXXX
和path.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
绘制,绘制部分时间大大减少。
通过JS引擎运行JS代码,调用本地Canvas方法实现绘制。 目前仅实现部分JS端Canvas接口,实现描述如下:
- 接口参数类型、顺序和数量均以JavaScript为准
- 属性设置由直接赋值转换为方法调用,如
ctx.lineWidth = 5
转换为ctx.setLineWidth(5)
,并暂时无法通过ctx.lineWidth
和ctx.getLineWidth()
获取 - 颜色统一为字符串形式色码,如"#188ffc"
createCanvas()
devicePixelRatio
setSize(x, y, width, height)
setX(x)
setY(y)
setWidth(width)
setHeight(height)
context
setFillColor(color)
setStrokeColor(color)
setLineWidth(width)
setLineDash(intervals)
fillRect(x, y, width, height)
strokeRect(x, y, width, height)
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)
translate(x, y)
transform(a, b, c, d, dx, dy)
scale(scaleWidth, scaleHeight)
rotate(angle)
setFontSize(fontSize)
fillText(text, x, y)
measureText(text)
setGlobalAlpha(alpha)
save()
restore()