美文网首页Android UI绘制
UI绘制-Paint(四)颜色过滤器 ColorFilter

UI绘制-Paint(四)颜色过滤器 ColorFilter

作者: tingtingtina | 来源:发表于2019-06-12 14:00 被阅读0次

系列文章传送门:
UI绘制-Paint(一)Paint基本属性及方法
UI绘制-Paint(二)颜色相关方法
UI绘制-Paint(三)图层混合模式
UI绘制-Paint(四)颜色过滤器 ColorFilter

mPaint.setColorFilter(ColorFilter filter);

设置颜色过滤,一般使用ColorFilter三个子类

  • LightingColorFilter 光照效果
  • PorterDuffColorFilter 指定一个颜色和一种PorterDuff.Mode 与绘制对象进行合成
  • ColorMatrixColorFilter 适应一个ColorMatix来对颜色进行处理

LightColorFilter滤镜

    /**
     *
     * <pre>
     * R' = R * colorMultiply.R + colorAdd.R
     * G' = G * colorMultiply.G + colorAdd.G
     * B' = B * colorMultiply.B + colorAdd.B
     * </pre>
     * @param mul 用来和目标像素相乘
     * @param add 用来和目标像素相加
     *
     */
    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 一维数组表示的4行5列的矩阵数组
     */
    public ColorMatrixColorFilter(@NonNull float[] array) {
  

色彩矩阵分析
在Android中,系统使用一个颜色矩阵ColorMatrix,来处理图形的色彩效果,对于图像的每个像素点,都有一个颜色分量矩阵的RGBA值(下图矩阵C),Android中的颜色矩阵是一个4×5的数字矩阵,他用来对图片的色彩进行处理(下图矩阵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系统中,我们会用矩阵的乘法运算来修改颜色分量矩阵的值,上面的矩阵就是一个4×5的颜色矩阵。在Android中,它会以一位数组的形式来存储[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]
根据线性代数可得

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

从公式可发现,矩阵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,同理其他通道也如此,则可构造出一个矩阵,如下:
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,根据矩阵乘法运算法则,可得R1 = R, G1 = G, B1 = B,A1 = A,即不会对原有的颜色进行任何修改,所以这个矩阵通常被用来作为初始颜色矩阵。
改变颜色值
那么,当我们想要改变颜色值的时候,通常有两种方法,

  • 改变颜色的 offset(偏移量)的值;
  • 改变对应的 RGBA 值的系数。

改变偏移量

从前面分析可知,改变颜色的偏移量就是改变颜色矩阵的第五列的值,其他保持初始矩阵的值即可,如下示例:
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对应颜色偏移量,那么结果是红色和绿色分量增加了100,即整体色调偏黄色。如下图左一

image.png

可以设置不同的矩阵系数来对图片进行不同滤镜效果

image.png

ColorMatrix

ColorMatrixColorFilter 还有一种构造参数,如下:

 /**
     * Create a color filter that transforms colors through a 4x5 color matrix.
     *
     * @param matrix 4行5列的矩阵数组
     */
    public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) 
        ColorMatrix cm = new ColorMatrix();
        //亮度调节
        cm.setScale(1,2,1,1);

        //饱和度调节0-无色彩, 1- 默认效果, >1饱和度加强
        cm.setSaturation(2);

        //色调调节
        cm.setRotate(0, 45);

        mColorMatrixColorFilter = new ColorMatrixColorFilter(cm);

亮度

  • 以setScale() 可进行RGB亮度调节,源码如下:
 /**
     *R,G,B,A四个通道的系数
     */
    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;
    }

可看出该方法实际也是操作初始矩阵的系数来达到相应效果的。

色调

Android系统提供了 setRotate(int axis, float degrees)方法来修改颜色的色调。第一个参数,用0、1、2分别代表红、绿、蓝三个颜色通道,第二个参数就是要修改的值,如下:

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

Android 提供了setRotate()方法,其实是对色彩的旋转运算,用R、G、B三色建立三维坐标系


image.png

这里,我们可以把一个色彩值看成三维空间里的一个点,色彩值的三个分量可以看成该点对应的坐标(三维坐标)。我们先不考虑,在三个维度综合情况下是怎么旋转的。我们先看看,以某个轴做为Z轴,以另两个轴形成的平面上旋转的情况。假如,我们现在需要围绕蓝色轴进行旋转,我们对着蓝色箭头观察由红色和绿色构造的平面。然后顺时针旋转 α 度。 如下图所示:

image.png

在图中,我们可以看到,在旋转后,原 R 在 R 轴的分量变为:Rcosα,且原G分量在旋转后在 R 轴上也有了分量,所以我们要加上这部分分量,因此最终的结果为 R’=Rcosα + Gsinα,同理,在计算 G’ 时,因为 R 的分量落在了负轴上,所以我们要减去这部分,故 G’=Gcosα - R*sinα;
我们可以计算出围绕蓝色分量轴顺时针旋转 α 度的颜色矩阵,如下:
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 时的矩阵系数,其他通道也如此。

 /**
     * 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) 方法可设置饱和度,其源码如下, 可看出该方法是通过改变颜色矩阵中对角线上系数来改变饱和度的,当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;
    }

相关文章

网友评论

    本文标题:UI绘制-Paint(四)颜色过滤器 ColorFilter

    本文链接:https://www.haomeiwen.com/subject/podbxctx.html