Transcript 软件绘图
Android图形系统简介
朱才
计算机图形学
图像
图像的承载:Bitmap、FrameBuffer都是点阵图。由每个像素排列构成。
通常的图像格式ARGB8888就表示每个通道8bit,每个像素占32bit=4byte。
如0x11223344表示11为alpha,22为red,33为green,44为blue。
颜色也是这么表示,可以认为每个像素就是一个颜色。
一个ARGB8888格式的2x2的纯红图片内存应该是这样的byte[]:(16进制表示)
ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00
计算机图形学的根本就是创建或修改图像内容。
计算机图形学
绘图 - 画点的伪代码
void 画点(x,y, 颜色) {
计算点在buffer中的索引: y * 宽度 + x;
改变buffer中指定索引处的值为指定颜色值。
}
计算机图形学
绘图 - 画直线的伪代码
void 画线(点1,点2) {
计算斜率;
画第一个点;
for循环() {
根据斜率,画下一个点,直到结束;
}
}
计算机图形学
绘图 - 画三角形的伪代码
void 画三角形(顶点1,顶点2,顶点3) {
绘制最上面的点;
计算上面的点到下面两个点的斜率;
for循环 (y:从上往下循环,直到碰到其中一个点后结束){
根据两个斜率,计算出三角形在当前y坐标时两条边上的x1,x2坐标;
for循环 (xn:从x1到x2) {
绘制点(xn,y);
}
}
上面是绘制的三角形上半部分,这里再用类似的方式绘制下半部分,过程同上。
}
计算机图形学
绘图 - 画图片的伪代码
void 画图片(顶点1,顶点2,顶点3,纹理坐标1,纹理坐标2,纹理坐标3,图片) {
根据纹理坐标取图片上对应像素来绘制最上面的点;
计算上面的点到下面两个点的斜率;
for循环 (y:从上往下循环,直到碰到其中一个点后结束){
根据点的两个斜率,计算出三角形在当前y坐标时两条边上的x1,x2坐标;
for循环 (xn:从x1到x2) {
根据当前点坐标,插值得到当前点的纹理坐标;
根据纹理坐标取图片上对应像素来绘制点:xn,y;
}
}
上面是绘制的三角形上半部分,这里再用类似的方式绘制下半部分,过程同上。
}
Android图形系统
Skia和OpenGL
Skia:
C++编写。CPU执行各种draw操作。2D。
支持绘制各种矢量图形。
OpenGL:
API规范,由各个硬件厂商提供实现。硬件加速。3D。
OpenGL ES可认为是精简版。
OpenGL ES只支持绘制:点、线、三角形。多边形一般由多个三角形拼成。
OpenGL ES 2.0之后支持Shader:GPU里执行的代码。
一次绘制的过程大概是:
输入图元及相关信息:包括顶点坐标、顶点颜色或顶点纹理坐标等
顶点变换:模型视图矩阵映射每个坐标顶点,投影矩阵将3D坐标映射为2D坐标。
渲染:根据绘制的类型(一般都是三角形)、顶点数据,来渲染其每个像素。
Android图形系统
App
SurfaceFlinger
Buffers
软件绘图:Skia实现
硬件绘图:hwui库 => OpenGL实现
Or
Skia
Buffers
Buffers
其他App
Buffers
其他App
hwui(OpenGL)
用OpenGL或Overlay方式合成,
输出到FrameBuffer
Android图形系统
ViewRootImpl
ViewGroup
mSurface.lockCanvas
得到Canvas
draw,传进去Canvas
软件绘图,app端一次绘图流程
View
draw,传进去Canvas
draw,在Canvas上绘制自己
View
draw,传进去Canvas
draw,在Canvas上绘制自己
mSurface.unlockCanvasAndPost将
Canvas的内容交给SurfaceFlinger
HardwareRenderer
Android图形系统
ViewGroup
view.getDisplayList()
canvas=mDisplayList.start()
this.dispatchDraw(canvas)
硬件绘图,app端一次绘图流程
View
child.draw(canvas,this,...)
displayList=getDisplayList()
canvas2=mDisplayList.start()
draw(canvas2)调用绘制自己的代码
canvas.drawDisplayList(displaylist,..)
返回一个displayList
初始化时创建的mCanvas.drawDisplayList(displayList,..)
eglSwapBuffers
Android图形系统
View.setLayerType:硬件层和软件层以及NONE
NONE:默认值。不是一个单独的层。软件绘图还是硬件绘图跟着父View走。
硬件层:一个独立的硬件层。里面的内容先会被渲染到一个离屏缓冲,再将这个缓冲内容绘制
到主缓冲区(FrameBuffer)。
软件层:会分配一个Bitmap,先将内容以软件绘图方式绘制到Bitmap中,再将Bitmap绘制到
主缓冲区。
层的主要好处:
内容未改变则不需要更新。
软件层里用软件绘图模式。
setLayerType方法有个参数Paint,用来最终将离屏缓冲绘制到主缓冲上。
Android图形系统
两种绘图模式的差异
软件绘图:
draw操作立即执行。
硬件绘图:
draw操作被缓存到DisplayList,获取整个DisplayList后一次性draw。
因此图片需要缓存。
图片需要加载到纹理。
更全面的介绍:http://developer.android.com/guide/topics/graphics/hardware-accel.html
Android图形API
Paint开关AntiAlias:文字、矢量图形(线、矩形等)使用。对Bitmap内部无效。
Android图形API
Paint开关FilterBitmap:图片平滑。图片有拉伸旋转时使用。(双线性过滤)
Android图形API
如果要绘制一个旋转的图片,怎样效果最好?
用drawBitmap,开启FilterBitmap,则边缘会有锯齿。(AntiAlias对drawBitmap无效。)
因此若要边缘也抗锯齿,则要用:BitmapShader(TileMode.CLAMP),drawRect方式,开启
FilterBitmap和AntiAlias。
Android图形API
Paint开关Dither:高精度格式图像绘制到低精度Canvas上时生效。如:RGB565的Canvas上绘制
ARGB8888的图片。
Android图形API
Canvas:holds the "draw" calls.
Matrix:holds a 3x3 matrix for transforming coordinates.
Paint:holds the style and color information about how to draw geometries, text and bitmaps.
Android图形API
Canvas.draw[point,line,rect,circle,oval,arc,path,text,bitmap]
Android图形API
Canvas.[translate、rotate、scale、skew] (都是pre的方式)
Matrix.set/pre/post[translate、rotate、scale、skew]
set:直接覆盖之前的值。
pre:前乘。后面的代码先生效。
post:后乘。前面的代码先生效。
Matrix通过mapPoints对关键点进行映射来产生效果。
Canvas.java: (可根据此函数的功能来理解前乘的效果)
public void scale(float scaleX, float scaleY, float pivotX, float pivotY) {
translate(pivotX, pivotY);
scale(scaleX, scaleY);
translate(-pivotX, -pivotY);
}
Android图形API
Matrix.setPolyToPoly:逆向,根据原始点与映射点计算得到Matrix。
Camera:3D投影。
Android图形API
Canvas
clipRect:裁剪区域。draw操作不会影响到超出的区域。
Android图形API
Canvas:
save,saveLayer。对应restore。
save:可保存Matrix和clip区域。
saveLayer:除了上面的,还会将指定区域分配成
离屏缓冲。(尽可能加
CLIP_TO_LAYER_SAVE_FLAG标志。)
GUI原理
GUI本质:用户输入和图像输出。
两者都建立在位置区域的基础上。
View里绘图时的本地坐标怎么来的:Canvas.translate。
为什么绘制无法超出View的内容:View.setClipChildren => Canvas.clipRect。
谢谢观看
Q&A
[email protected]