skia draw

最近终于有时间去看skia draw的一些过程,参考skia/gm和skia/samplecode里面的示例非常粗略的总结了一下skia的绘制过程,只能说建立一个概览吧。希望有熟悉skia的大神在不对之处可以帮忙指点。


skia的每次绘制都是由canvas.drawXX方法发起,经过path generation、rasterizer、shading和transfer四个步骤完成一张图的绘制。

Path generation

在skia绘制管线中,第一步工作就是要生成SkPah。所谓path,字面上讲是绘制路径,其实就是绘制形状。这部分工作可以分为三个阶段去完成:
第一阶段:初始化path。可以使用SkPath的moveTo(),lineTo(),conicTo(),quadto()等方法手动绘制线段、二次曲线等,以此来绘制物体的轮廓;也可以使用canvas的draw api来绘制矩形、椭圆等形状完成轮廓绘制。
第二阶段:为path添加效果。skia支持的效果有:添加线宽、corner、虚线、DiscretePath、1Dpath、2Dpath等。这部分可以参考pathEffect.cpp。
第三阶段:使用SkPath的setStyle()方法确定path的风格。
enum Style {
        kFill_Style,            //!< fill the geometry填充
        kStroke_Style,          //!< stroke the geometry绘制轮廓
        kStrokeAndFill_Style,   //!< fill and stroke the geometry
    };

Rasterizer

生成path之后的工作就是要去rasterizer。skia中的rasterizer主要是确定像素要画在哪,这通过使用一个mask图像来完成。mask图像实际上是一个只有alpha通道的灰度图。mask图像可以决定每个像素是全透明、不透明还是部分透明。

可以使用两个方法生成mask:

(1)no rasterizer

使用paint style property和path property进行path扫描转换,产生一个初始的mask。在path内部的像素是不透明的,path外部的像素保持透明,在path边界上的像素部分透明(如果设置anti-aliasing抗锯齿)。

如果设置了maskFilter,初始的mask会根据它进行变换,比如blur、emboss、table等效果。设置步骤为:首先使用SkPaint方法setMaskFilter()为paint分配一个SkMaskFilter;然后具体的mask过滤由SkMaskFilter子类实现。

对于blur效果,可以参考sampleBlur.cpp。

emboss效果,可以参考sampleEmboss.cpp。

(2)rasterizer

由SkRasterizer创建一个mask。

首先使用SkPaint的方法setRasterizer()为paint设置一个SkRasterizer;然后SkRasterizer的rasterize()方法会创建一个mask bitmap,之后把path绘制到这个mask中。

Shading

rasterizer之后就需要进行shading。shading的主要工作是确定像素的颜色,即着色。skia主要支持四种shader:线性梯度shader、环形梯度shader、扫略梯度shader以及混合shader。混合shader用来处理使用两种相同或者不同的shader在xfermode下的混合着色。这部分可以参考sampleShader.cpp例子。

着色过程有两个阶段:第一个阶段是为SkPaint分配一个SkShader,同样也是使用SkPaint的setShader方法。SkShader会生成一个初始的源图像。

在创建shader时,需要指定平铺模式(TileMode),有三种:kClamp_TileMode、kRepeat_TileMode和kMirror_TileMode,分别对应普通平铺、重复平铺和镜像平铺。镜像平铺很好理解,对于普通平铺,如果shader超出着色边界,则会使用边界的颜色延伸到bitmap边界;对于重复平铺,如果shader超出着色边界,则会在边界处重复之前的着色。

如果在这个阶段没有设置SkShader,则需要给paint设置一个color,此时会产生一张单色图像。


第二个阶段:如果为Skpaint设置了colorFilter,则colorFilter会为初始源图像的颜色进行变换。maskFilter与colorFilter不同:maskFilter是对一个paint的alpha通道的转换;而colorFilter是针对RGB通道的转换。所有由colorFilter所派生的类在执行它们的转换时都会忽略alpha通道。

(1)colorFilter可以通过改变colorMatrix进行色彩变换(C1=pin(C*Cm+Ca))。

a ,b ,c, d, e,

f ,g ,h ,i , j,

k ,l, m, n, o,

p ,q ,r ,s ,t

颜色矩阵M的第一行参数abcde决定了图像的红色成分,第二行参数fghij决定了图像的绿色成分,第三行参数klmno决定了图像的蓝色成分,第四行参数pqrst决定了图像的透明度,第五列参数ejot是颜色的偏移量。在Android中,对图像进行颜色方面的处理,如黑白老照片、泛黄旧照片、高对比度、低饱和度等效果,都可以通过使用颜色矩阵(ColorMatrix)来实现。
这部分的例子可以参考colorFilters.cpp。

(2)colorFilter也可以通过处理各种XforMode(图像混合模式)来针对dst/src图像进行色彩处理。

这部分的例子可以参考sampleColorFilters.cpp。

Transfer

这部分的工作是Transferring color to the destination Bitmap。我理解为把由rasterizer生成的mask、shader生成的源color图像、XferMode和目的图像进行混合处理,然后把最终的图像搬移到目的bitmap中。这部分也分为两个阶段进行:
第一阶段:把shader生成的源color图像与目的图像使用XferMode生成中间图像。
对于XferMode,我的理解是一种图像的混合模式,它主要有以下模式:
<pre name="code" class="cpp"><pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;">/* Sa 代表source alpha ,即源 alpha 值 ,
Da 代表 Destination alpha ,即 目标alpha值 ,
Sc 代表 source color ,即源色值 ,
Dc 代表 Destination color ,即目标色值,并且这所有的计算都以像素为单位.
[a, c]代表在某一种混合模式下,对每一个像素的alpha 和 color 通过对应算法进行运算,所得出的像素值*/
enum Mode {
        kClear_Mode,    //!< [0, 0]清除模式[0,0],即最终所有点的像素的alpha 和color 都为 0,所以画出来的效果只有白色背景
        kSrc_Mode,      //!< [Sa, Sc]只保留源图像的 alpha 和 color ,所以绘制出来只有源图
        kDst_Mode,      //!< [Da, Dc]同上类比,只保留目标图像的 alpha 和 color,所以绘制出来的只有目标图
        kSrcOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Sc + (1 - Sa)*Dc]在目标图片顶部绘制源图像,从命名上也可以看出来就是把源
			//图像绘制在上方
        kDstOver_Mode,  //!< [Sa + Da - Sa*Da, Rc = Dc + (1 - Da)*Sc]将目标图像绘制在上方
        kSrcIn_Mode,    //!< [Sa * Da, Sc * Da]在两者相交的地方绘制源图像,并且绘制的效果会受到目标图像对应地方透明度的影响
        kDstIn_Mode,    //!< [Sa * Da, Sa * Dc]在两者相交的地方绘制目标图像,并且绘制的效果会受到源图像对应地方透明度的影响
        kSrcOut_Mode,   //!< [Sa * (1 - Da), Sc * (1 - Da)]在不相交的地方绘制源图像,相交处根据目标alpha进行过滤,目标色完全
			//不透明时则完全过滤,完全透明则不过滤
        kDstOut_Mode,   //!< [Da * (1 - Sa), Dc * (1 - Sa)]类似上面
        kSrcATop_Mode,  //!< [Da, Sc * Da + (1 - Sa) * Dc]源图像和目标图像相交处绘制源图像,不相交的地方绘制目标图像,并且相
			//交处的效果会受到源图像和目标图像alpha的影响
        kDstATop_Mode,  //!< [Sa, Sa * Dc + Sc * (1 - Da)]类似上面
        kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]在不相交的地方按原样绘制源图像和目标图像,
			//相交的地方受到对应alpha和色值影响,按上面公式进行计算,如果都完全不透明则相交处完全不绘制
        kPlus_Mode,     //!< [Sa + Da, Sc + Dc]
        kModulate_Mode, // multiplies all components (= alpha and color)
        // Following blend modes are defined in the CSS Compositing standard:
        // https://dvcs.w3.org/hg/FXTF/rawfile/tip/compositing/index.html#blending
        kScreen_Mode,   //滤色,滤色模式与我们所用的显示屏原理相同,所以也有版本把它翻译成“屏幕”;简单的说就是保留两个图层中较
		        //白的部分,较暗的部分被遮盖;当一层使用了滤色(屏幕)模式时,图层中纯黑的部分变成完全透明,纯白部分完全
			//不透明,其他的颜色根据颜色级别产生半透明的效果
        kLastCoeffMode = kScreen_Mode,

        kOverlay_Mode,  //像素是进行 Multiply (正片叠底)混合还是 Screen (屏幕)混合,取决于底层颜色,但底层颜色的高光与阴影部
			//分的亮度细节会被保留
        kDarken_Mode,   //该模式处理过后,会感觉效果变暗,即进行对应像素的比较,取较暗值,如果色值相同则进行混合;从算法上看,
			//alpha值变大,色值上如果都不透明则取较暗值,非完全不透明情况下使用上面算法进行计算,受到源图和目标图对应
			//色值和alpha值影响
        kLighten_Mode,  //DARKEN 的目的是变暗,LIGHTEN 的目的则是变亮,如果在均完全不透明的情况下 ,色值取源色值和目标色值中的较大值,
			//否则按上面算法进行计算
        kColorDodge_Mode,
        kColorBurn_Mode,
        kHardLight_Mode,
        kSoftLight_Mode,
        kDifference_Mode,
        kExclusion_Mode,
        kMultiply_Mode, //正片叠底,即查看每个通道中的颜色信息,并将基色与混合色复合。结果色总是较暗的颜色。任何颜色与黑色复合产生黑色。
			//任何颜色与白色复合保持不变。当用黑色或白色以外的颜色绘画时,绘画工具绘制的连续描边产生逐渐变暗的颜色
        kLastSeparableMode = kMultiply_Mode,

        kHue_Mode,
        kSaturation_Mode,
        kColor_Mode,
        kLuminosity_Mode,
        kLastMode = kLuminosity_Mode
    };</span>
 
 
 
 有关XferMode的例子可以参考xfermode.cpp、xfermode2.cpp、xfermode3.cpp、sampleXfermode.cpp。 
注意:skia默认的XferMode是kSrcOver_Mode。

第二阶段:使用由rasterizer生成的mask把中间图像和目的图像再一次混合处理,然后把搬移到目的bitmap。(这部分暂时还没有看到)

总结

大体看完这部分,感觉skia在draw的过程中,首先要使用path + rasterizer得到绘制的图元形状,然后通过shader进行着色处理,最后transfer的过程主要用来将src/dst图元完成混合处理,并将最终图像搬移到目的bitmap中。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值