Canvas&Paint 知識梳理(3) 顏色合成 Paint#setColorFilter

澤毛發表於2017-12-21

一、概述

有時候,我們希望對一個圖片或一個複雜圖形的顏色,進行處理,那麼這時候可以採用PaintsetColorFilter方法,一個最常見的例子,就是圖片的濾鏡,當然,那裡面的演算法可能更加複雜。

二、ColorFilter的分類

關於ColorFilter,原始碼中是這麼解釋的,它可以Paint所繪製區域的每個畫素進行顏色的改變

/**
 * A color filter can be used with a {@link Paint} to modify the color of
 * each pixel drawn with that paint. This is an abstract class that should
 * never be used directly.
 */
複製程式碼

當我們使用ColorFilter的時候,不一樣直接使用它,而是使用它的子類:

  • ColorMatrixColorFilter
  • LightingColorFilter
  • PoterDuffColorFilter

2.1 ColorMatrixColorFilter

ColorMatrixColorFilter的通過ColorMatrix構造,而ColorMatrix則由一個長度為20float陣列構造,傳入該陣列後把該陣列先從左到右,再從上到下排列,形成一個4*5的矩陣。

[ a, b, c, d, e,
  f, g, h, i, j,
  k, l, m, n, o,
  p, q, r, s, t ]
複製程式碼

之後,再用矩陣和目標的RGBA進行計算,最後得到新的RGBA,它的計算方法為:

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;
複製程式碼

注意,新的RGBA會被限制在0-255的範圍內。在實際使用的時候,我們先通過一個ColorMatrix輔助類來確定需要相乘的這個顏色矩陣,之後再把它作為ColorMatrixColorFilter建構函式的引數來構造它。 我們一般有兩種用法:改變畫筆的顏色,或者改變整個Bitmap的畫素點的顏色。下面我們用第二種方式來舉例。

    private void drawColorMatrixFilter(Canvas canvas) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        //1,得到一個顏色矩陣
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.setSaturation(0.5f);
        //2.通過顏色矩陣構建ColorMatrixColorFilter物件
        ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);
        Paint matrixPaint = new Paint();
        //3.把構建的物件設定給Paint
        matrixPaint.setColorFilter(colorMatrixColorFilter);
        canvas.drawBitmap(bitmap, 0, 0, matrixPaint);
    }
複製程式碼

最終我們會得到一個蒙了灰的圖片:

Canvas&Paint 知識梳理(3)   顏色合成 Paint#setColorFilter
當然我們也可以先設定畫筆的顏色,然後給它設定一個顏色矩陣,這樣最後花上去的圖形的就是就是等於畫筆顏色和矩陣一起計算的結果。

2.2 LightingColorFilter

用來模擬光照的效果,它定義了兩個引數來和原color相乘,第一個colorMultiply原來相乘,而第二個引數colorAdd用來相加,並且會忽略其中的Aplha引數,這個32位的表示0xAARRGGBB,計算的公式為:

R' = R * colorMultiply.R + colorAdd.R
G' = G * colorMultiply.G + colorAdd.G
B' = B * colorMultiply.B + colorAdd.B
複製程式碼

需要注意的是這個倍數為整形,也就是我們只能增大,不能減小。

    private void drawLightingColorFilter(Canvas canvas) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        //1.構建一個LightingColorFilter
        LightingColorFilter lightingColorFilter = new LightingColorFilter(3, 0);
        Paint matrixPaint = new Paint();
        //2.設定給畫筆
        matrixPaint.setColorFilter(lightingColorFilter);
        //3.繪製
        canvas.drawBitmap(bitmap, 0, 0, matrixPaint);
    }
複製程式碼

最終會得到下面的結果:

Canvas&Paint 知識梳理(3)   顏色合成 Paint#setColorFilter
可以看到,對於原本RGB0的畫素點,它不會做任何改變,它只會改變那些原本有顏色的畫素點。

2.3 PorterDuffColorFilter

混合模式,其建構函式為PorterDuffColorFilter(int dstColor, PorterDuff.Mode mode),其中color16進位制的終點顏色,mode為混合策略,它會根據起點顏色Sc,起點透明度Sa,終點顏色Dc和終點透明度Da最終計算得出要顯示的RGBA

 * A color filter that can be used to tint the source pixels using a single
 * color and a specific {@link PorterDuff Porter-Duff composite mode}.
複製程式碼

其中modePorterDuff.Mode,其對應的模式和計算公式如下:

public enum Mode {
        /** [0, 0] */
        CLEAR       (0),
        /** [Sa, Sc] */
        SRC         (1),
        /** [Da, Dc] */
        DST         (2),
        /** [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] */
        SRC_OVER    (3),
        /** [Sa + (1 - Sa)*Da, Rc = Dc + (1 - Da)*Sc] */
        DST_OVER    (4),
        /** [Sa * Da, Sc * Da] */
        SRC_IN      (5),
        /** [Sa * Da, Sa * Dc] */
        DST_IN      (6),
        /** [Sa * (1 - Da), Sc * (1 - Da)] */
        SRC_OUT     (7),
        /** [Da * (1 - Sa), Dc * (1 - Sa)] */
        DST_OUT     (8),
        /** [Da, Sc * Da + (1 - Sa) * Dc] */
        SRC_ATOP    (9),
        /** [Sa, Sa * Dc + Sc * (1 - Da)] */
        DST_ATOP    (10),
        /** [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc] */
        XOR         (11),
        /** [Sa + Da - Sa*Da,
             Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)] */
        DARKEN      (16),
        /** [Sa + Da - Sa*Da,
             Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)] */
        LIGHTEN     (17),
        /** [Sa * Da, Sc * Dc] */
        MULTIPLY    (13),
        /** [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] */
        SCREEN      (14),
        /** Saturate(S + D) */
        ADD         (12),
        OVERLAY     (15);

        Mode(int nativeInt) {
            this.nativeInt = nativeInt;
        }

        /**
         * @hide
         */
        public final int nativeInt;
    }
複製程式碼

其中,Sa表示起點的Alpha值,而Sc表示起點的color值,對於Dx也是同理,最終會得到[Ra, Rc],這樣組成之後就是終點對應畫素點的顏色。

    private void drawPorterDuffColorFilter(Canvas canvas) {
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
        Paint paint = new Paint();
        paint.setColorFilter(new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN));
        canvas.drawBitmap(bitmap, 0, 0, paint);
    }
複製程式碼

最終會得到下面的結果:

Canvas&Paint 知識梳理(3)   顏色合成 Paint#setColorFilter
後面我們會發現PorterDuff.Mode中定義的這些Mode不僅僅用於顏色合成,還用於影像合成。

相關文章