UI繪制-Paint(四)顏色過濾器 ColorFilter

系列文章傳送門:
UI繪制-Paint(一)Paint基本屬性及方法
UI繪制-Paint(二)顏色相關(guān)方法
UI繪制-Paint(三)圖層混合模式
UI繪制-Paint(四)顏色過濾器 ColorFilter

mPaint.setColorFilter(ColorFilter filter);

設(shè)置顏色過濾,一般使用ColorFilter三個子類

  • LightingColorFilter 光照效果
  • PorterDuffColorFilter 指定一個顏色和一種PorterDuff.Mode 與繪制對象進行合成
  • ColorMatrixColorFilter 適應(yīng)一個ColorMatix來對顏色進行處理

LightColorFilter濾鏡

    /**
     *
     * <pre>
     * R' = R * colorMultiply.R + colorAdd.R
     * G' = G * colorMultiply.G + colorAdd.G
     * B' = B * colorMultiply.B + colorAdd.B
     * </pre>
     * @param mul 用來和目標(biāo)像素相乘
     * @param add 用來和目標(biāo)像素相加
     *
     */
    public LightingColorFilter(@ColorInt int mul, @ColorInt int add)

如:

        //紅色去除掉
        LightingColorFilter lighting = new LightingColorFilter(0x00ffff,0x000000);
        mPaint.setColorFilter(lighting);
        canvas.drawBitmap(mBitmap, 0,0, mPaint);

        //原始圖片效果
        LightingColorFilter lighting = new LightingColorFilter(0xffffff,0x000000);
        mPaint.setColorFilter(lighting);
        canvas.drawBitmap(mBitmap, 0,0, mPaint);

        //綠色更亮
        LightingColorFilter lighting = new LightingColorFilter(0xffffff,0x003000);
        mPaint.setColorFilter(lighting);
        canvas.drawBitmap(mBitmap, 0,0, mPaint);
LightColorFilter

PorterDuffColorFilter濾鏡

  /**
     * Create a color filter that uses the specified color and Porter-Duff mode.
     *
     * @param color 具體的顏色值,例如Color.RED
     * @param mode 指定PorterDuff.Mode 混合模式
     */
    public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) 
        PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN);
        mPaint.setColorFilter(porterDuffColorFilter);
        canvas.drawBitmap(mBitmap, 100, 100, mPaint);
PorterDuffColorFilter

可以看出, 前一篇的混合模式是圖層和圖層進行混合,而此處是顏色和圖層混合。

ColorMatrixColorFilter

    /**
     * Create a color filter that transforms colors through a 4x5 color matrix.
     *
     * @param array 一維數(shù)組表示的4行5列的矩陣數(shù)組
     */
    public ColorMatrixColorFilter(@NonNull float[] array) {
  

色彩矩陣分析
在Android中,系統(tǒng)使用一個顏色矩陣ColorMatrix,來處理圖形的色彩效果,對于圖像的每個像素點,都有一個顏色分量矩陣的RGBA值(下圖矩陣C),Android中的顏色矩陣是一個4×5的數(shù)字矩陣,他用來對圖片的色彩進行處理(下圖矩陣A),如下

A= \left[ \begin{matrix} a & b & c & d &e\\ f & g & h & i & j \\ k & l & m & n & o \\ p & q & r & s & t \end{matrix} \right] C= \left[ \begin{matrix} R\\ G\\ B \\ A\\ 1 \end{matrix} \right]

如果我們想要改變一張圖像的色彩顯示效果。在Android系統(tǒng)中,我們會用矩陣的乘法運算來修改顏色分量矩陣的值,上面的矩陣就是一個4×5的顏色矩陣。在Android中,它會以一位數(shù)組的形式來存儲[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],而C則是一個顏色矩陣的分量。在處理圖像時,使用矩陣乘法運算AC來處理顏色分量矩陣,如下:
R = AC = \left[ \begin{matrix} a & b & c & d &e\\ f & g & h & i & j \\ k & l & m & n & o \\ p & q & r & s & t \end{matrix} \right] \left[ \begin{matrix} R\\ G\\ B \\ A\\ 1 \end{matrix} \right]= \left[ \begin{matrix} aR & bG & cB & dA &e\\ fR & gG & hB & iA & j \\ kR & lG & mB & nA & o \\ pR & qG & rB & sA & t \end{matrix} \right]= \left[ \begin{matrix} R1\\ G1\\ B1 \\ A1 \end{matrix} \right]
根據(jù)線性代數(shù)可得

R1 = aR + bG + cB + dA + e;
G1 = fR + gG + hB + iA + j;
B1 = kR + lG + mB + nA + o;
A1 = pR + qG + rB + sA + t;

從公式可發(fā)現(xiàn),矩陣A中

  • 第一行的abcde用來決定新的顏色值中的R --- 紅色
  • 第二行的fghij用來決定新的顏色值中的G --- 綠色
  • 第三行的klmno用來決定新的顏色值中的B --- 藍色
  • 第四行的pqrst用來決定新的顏色值中的A --- 透明度
  • 矩陣中的第五列(ejot)分別用來決定給每個分量中的offset,即偏移量
    這樣劃分好后,這些值作用就比較明確了

初始顏色矩陣

接下來,我們重新看一下矩陣變換的計算公式,以R分量為例

R1 = aR + bG + cB + dA + e;

如果令a = 1,b = c = d = e = 0,則 R1 = R,同理其他通道也如此,則可構(gòu)造出一個矩陣,如下:
A= \left[ \begin{matrix} 1 & 0 & 0 & 0 &0\\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right]
將這個矩陣帶入公式R = AC,根據(jù)矩陣乘法運算法則,可得R1 = R, G1 = G, B1 = B,A1 = A,即不會對原有的顏色進行任何修改,所以這個矩陣通常被用來作為初始顏色矩陣。
改變顏色值
那么,當(dāng)我們想要改變顏色值的時候,通常有兩種方法,

  • 改變顏色的 offset(偏移量)的值;
  • 改變對應(yīng)的 RGBA 值的系數(shù)。

改變偏移量

從前面分析可知,改變顏色的偏移量就是改變顏色矩陣的第五列的值,其他保持初始矩陣的值即可,如下示例:
A= \left[ \begin{matrix} 1 & 0 & 0 & 0 &100 \\ 0 & 1 & 0 & 0 & 100 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right]
上面改變了R、G對應(yīng)顏色偏移量,那么結(jié)果是紅色和綠色分量增加了100,即整體色調(diào)偏黃色。如下圖左一

image.png

可以設(shè)置不同的矩陣系數(shù)來對圖片進行不同濾鏡效果

image.png

ColorMatrix

ColorMatrixColorFilter 還有一種構(gòu)造參數(shù),如下:

 /**
     * Create a color filter that transforms colors through a 4x5 color matrix.
     *
     * @param matrix 4行5列的矩陣數(shù)組
     */
    public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) 
        ColorMatrix cm = new ColorMatrix();
        //亮度調(diào)節(jié)
        cm.setScale(1,2,1,1);

        //飽和度調(diào)節(jié)0-無色彩, 1- 默認效果, >1飽和度加強
        cm.setSaturation(2);

        //色調(diào)調(diào)節(jié)
        cm.setRotate(0, 45);

        mColorMatrixColorFilter = new ColorMatrixColorFilter(cm);

亮度

  • 以setScale() 可進行RGB亮度調(diào)節(jié),源碼如下:
 /**
     *R,G,B,A四個通道的系數(shù)
     */
    public void setScale(float rScale, float gScale, float bScale,
                         float aScale) {
        final float[] a = mArray;

        for (int i = 19; i > 0; --i) {
            a[i] = 0;
        }
        a[0] = rScale;
        a[6] = gScale;
        a[12] = bScale;
        a[18] = aScale;
    }

可看出該方法實際也是操作初始矩陣的系數(shù)來達到相應(yīng)效果的。

色調(diào)

Android系統(tǒng)提供了 setRotate(int axis, float degrees)方法來修改顏色的色調(diào)。第一個參數(shù),用0、1、2分別代表紅、綠、藍三個顏色通道,第二個參數(shù)就是要修改的值,如下:

ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0, hue0);
hueMatrix.setRotate(1, hue1);
hueMatrix.setRotate(2, hue2);

Android 提供了setRotate()方法,其實是對色彩的旋轉(zhuǎn)運算,用R、G、B三色建立三維坐標(biāo)系


image.png

這里,我們可以把一個色彩值看成三維空間里的一個點,色彩值的三個分量可以看成該點對應(yīng)的坐標(biāo)(三維坐標(biāo))。我們先不考慮,在三個維度綜合情況下是怎么旋轉(zhuǎn)的。我們先看看,以某個軸做為Z軸,以另兩個軸形成的平面上旋轉(zhuǎn)的情況。假如,我們現(xiàn)在需要圍繞藍色軸進行旋轉(zhuǎn),我們對著藍色箭頭觀察由紅色和綠色構(gòu)造的平面。然后順時針旋轉(zhuǎn) α 度。 如下圖所示:

image.png

在圖中,我們可以看到,在旋轉(zhuǎn)后,原 R 在 R 軸的分量變?yōu)椋篟cosα,且原G分量在旋轉(zhuǎn)后在 R 軸上也有了分量,所以我們要加上這部分分量,因此最終的結(jié)果為 R’=Rcosα + Gsinα,同理,在計算 G’ 時,因為 R 的分量落在了負軸上,所以我們要減去這部分,故 G’=Gcosα - R*sinα;
我們可以計算出圍繞藍色分量軸順時針旋轉(zhuǎn) α 度的顏色矩陣,如下:
A= \left[ \begin{matrix} cosα & sinα & 0 & 0 &0\\ cosα & -sinα & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \end{matrix} \right]
符合 axis = 2 時的矩陣系數(shù),其他通道也如此。

 /**
     * Set the rotation on a color axis by the specified values.
     * <p>
     * <code>axis=0</code> correspond to a rotation around the RED color
     * <code>axis=1</code> correspond to a rotation around the GREEN color
     * <code>axis=2</code> correspond to a rotation around the BLUE color
     * </p>
     */
    public void setRotate(int axis, float degrees) {
        reset();
        double radians = degrees * Math.PI / 180d;
        float cosine = (float) Math.cos(radians);
        float sine = (float) Math.sin(radians);
        switch (axis) {
        // Rotation around the red color
        case 0:
            mArray[6] = mArray[12] = cosine;
            mArray[7] = sine;
            mArray[11] = -sine;
            break;
        // Rotation around the green color
        case 1:
            mArray[0] = mArray[12] = cosine;
            mArray[2] = -sine;
            mArray[10] = sine;
            break;
        // Rotation around the blue color
        case 2:
            mArray[0] = mArray[6] = cosine;
            mArray[1] = sine;
            mArray[5] = -sine;
            break;
        default:
            throw new RuntimeException();
        }
    }

飽和度

setSaturation(float sat) 方法可設(shè)置飽和度,其源碼如下, 可看出該方法是通過改變顏色矩陣中對角線上系數(shù)來改變飽和度的,當(dāng)sat = 0,無色彩,即黑白; sat = 1, 默認效果(初始矩陣),sat >1,飽和度加強

 /**
     * Set the matrix to affect the saturation of colors.
     *
     * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
     */
    public void setSaturation(float sat) {
        reset();
        float[] m = mArray;

        final float invSat = 1 - sat;
        final float R = 0.213f * invSat;
        final float G = 0.715f * invSat;
        final float B = 0.072f * invSat;

        m[0] = R + sat; m[1] = G;       m[2] = B;
        m[5] = R;       m[6] = G + sat; m[7] = B;
        m[10] = R;      m[11] = G;      m[12] = B + sat;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容