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中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章