Skia
绘图的流程1
,API
用法(1)drawBitmap
void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top, const SkPaint* paint = NULL);
把位图
画到x,y
的位置(自身是个平移
,需要叠加SkCanvas
中的矩阵状态
).
(2)drawBitmapRect
和drawBitmapRectToRect
void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint = NULL);
void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint* paint, DrawBitmapRectFlags flags);
把src
源图矩阵
,画到目标dst
区域.
最后flags
是安卓
上为了gpu
绘画效果而加上去的,在CPU
绘画中不需要关注.
(3)drawSprite
void drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint* paint);
无视SkCanvas
的矩阵状态,把位图
平移到x,y
的位置.
(4)drawBitmapMatrix
void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint);
绘画带矩形
变换的位图
,需要叠加SkCanvas
的矩形变换
.
(5)drawRect
void drawRect(const SkRect& r, const SkPaint& paint);
最通用
方法,要加额外效果
时常用,如要绘画
重复纹理.Tile
的两个参数
是,OpenGL
纹理贴图中水平垂直
方向上的边界处理
模式.
比如画圆角矩形
图标时,把方图片
裁剪成圆等.
下面是个示例
程序:
#include "SkBitmapProcShader.h"
#include "SkCanvas.h"
#include "SkBitmap.h"
#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkRect.h"
int main()
{
const int w = 1080;
const int h = 1920;
/*准备目标图片和源图片*/
SkBitmap dst;
dst.allocPixels(SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType));
SkCanvas c(dst);
SkBitmap src;
SkImageDecoder::DecodeFile("test.jpg", &src);
/*各种绘图方法使用示例*/
{
c.drawBitmap(src, 0, 0, NULL);
}
{
c.drawSprite(src, 400, 400, NULL);
}
{
SkRect dstR;
r.set(29, 29, 100, 100);
SkRect srcR;
r.set(0,0,40,50);
c.drawBitmapRectToRect(src, &srcR, dstR, NULL);
}
{
SkMatrix m;
m.setScale(1.4,4.3);
c.drawBitmapMatrix(src, m, NULL);
}
{
SkRect dstRect;
dstRect.set(100,100,480,920);
SkPaint paint;
SkMatrix m;
m.setScale(3.2, 4.1);
SkShader* shader = CreateBitmapShader(src, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, m, NULL);
paint.setShader(shader);
SkSafeUnref(shader);
c.drawRect(dstRect, paint);
}
/*输出图片*/
SkImageEncoder::EncodeFile("output.jpg", dst, SkImageEncoder::kJPEG_Type, 100);
return 1;
}
2
,解析流程(1)SkCanvas
两重循环调用SkBitmapDevice
,进而调用SkDraw
.
在SkDraw
中,drawBitmap
的渲染
函数统一为:
void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix, const SkPaint& origPaint) const;
(2)Sprite
简易模式满足如下条件时,为Sprite
简易模式.
代码见external/skia/src/core/SkDraw.cpp
的drawBitmap
函数.
a:
(bitmap.colorType() != kAlpha_8_SkColorType && just_translate(matrix, bitmap))
kAlpha_8_SkColorType
的图像只有一个α
通道,按drawMask
方式处理,按图像的α
预乘绘图
中的颜色
,再叠加
到目标区域
上.
just_translate
表示矩形
为不涉及旋转缩放
的仅平移
矩阵,位图
的像素点
和SkCanvas
绑定的dstBitmap
的像素点
一一对应.
b:
clipHandlesSprite(*fRC, ix, iy, bitmap))
指当前SkCanvas
的裁剪区域
不需要考虑抗锯齿
,或完全包含了位图
的渲染区域
.必须在裁剪区域
内渲染SkCanvas
,因此如果图像
跨越了裁剪区域
边界,且裁剪区域
要考虑抗锯齿
,则在边界
上要特殊处理.
注:设置裁剪区域
的API
:
void SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA)
doAA
即是否在r
的非整数边界
时考虑抗锯齿
.
满足条件,由SkScan::FillIRect
按每个裁剪区域
调用SkSpriteBlitter
的blitRect
来创建SkSpriteBlitter
.
此时可直接转换颜色
及合成透明度
,来渲染,不必抗锯齿和图像插值
,也就不必取样--混合
,性能最高
.
满足条件后用ChooseSprite
去选一个SkSpriteBlitter
详细代码见external/skia/src/core/SkBlitter_Sprite.cpp
中的ChooseSprite
函数.
一般不用此函数
,然后就开始转回drawRect
流程.
(3)
创建BitmapShader
在:
SkAutoBitmapShaderInstall install(bitmap, paint);
句中,为绘图
创建了bitmapShader
:
fPaint.setShader(CreateBitmapShader(src, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, localMatrix, &fAllocator));
然后,就可用drawRect
画图像了.
(4)drawRect
不能用SkSpriteBlitter
来drawRect
.
这里有非常多的分支,只讲绘画实矩形
.
在SkAutoBlitterChoose->SkBlitter::Choose
中,根据Canvas
绑定的位图
像素模式,绘图
属性选择blitter
.
绘图时绘图
有Shader(SkBitmapProcShader)
,因此是选的是带Shader
的Blitter
,比如适应ARGB
格式的SkARGB32_Shader_Blitter
.
(5)SkScan
SkScan
中,对每个裁剪区域
,将其与绘画的rect
求交,然后渲染
该相交区域
.此外,必要时抗锯齿
.
抗锯齿
就是对浮点的坐标
,按其偏离整数的程度
给一个α
权重,颜色
再乘以此权重
(减淡颜色
),再画上去.
在绘画矩形时,SkScan
先用blitV
绘画左右边界
,再用blitAntiH
绘画上下边界
,中间大块
不需要考虑抗锯齿
,因而用blitRect
.
(6)blitRect
这一步先通过Shader
的shadeSpan
方法取对应位置
的像素,再通过SkBlitRow
的过程
叠加此像素
.
如果不考虑混合模式
,可跳过过程
.
参考代码:external/skia/src/core/SkBlitter_ARGB32.cpp
中的blitRect
(7)shadeSpan
这里只考虑SkBitmapProcShader
的shadeSpan
,这主要是图像采样
.详细代码见external/skia/src/core/SkBitmapProcShader.cpp
对每一个目标点
,先通过matrixProc
取出需要参考的源图像素
,然后用sampleProc
合成这些像素
为一个像素值.(和OpenGL
里面的texture2D
函数原理类似
).
若存在shaderProc
(线性插值
时,可优化上面
的步骤,完全可取出一群像素
来计算插值
),以shaderProc
代替上面的两步流程,来优化性能
.
3
,解析SkBlitter
接口(1)blitH
virtual void blitH(int x, int y, int width);
从x,y
坐标开始,渲染一行宽
像素
(2)blitV
virtual void blitV(int x, int y, int height, SkAlpha alpha);
从x,y
开始,渲染一列高
像素,按α
值减淡
颜色
(3)blitAntiH
virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
该函数的用来渲染上下边界
,并搞抗锯齿
.
(4)blitRect
virtual void blitRect(int x, int y, int width, int height);
绘画
矩形区域,这里不搞几何变换,抗锯齿
.
(5)blitMask
virtual void blitMask(const SkMask& mask, const SkIRect& clip);
绘画
文字时使用,颜色乘以掩码
中的透明度
,再叠加.