自定义View——Paint 之 ColorMatrix

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pzm1993/article/details/81217861

前言

每当遇到 ColorMatrix 这个东东,感觉头多大,什么矩阵、阶乘。。。放弃!

不敢面对,恐惧依在!所以,这次下决心要好好理解下,为了下次遇见,可以高傲的无视~

好了,废话多了,开始吧。

介绍

什么是矩阵阶乘?

这里截取百度百科里面的定义:

图片描述

看了这个图,琢磨一下,应该大体知道是怎么计算的。简单的说下:

A的第一行所有元素 与 B的第一列所有元素,分别相乘,最后结果相加(即:A1×B1 + A2×B2+ A3×B3),作为C矩阵第一行第一列的元素。

其他元素的计算也是类似,以此类推。

所以最终公式就是:

乘积C的第m行第n列的元素 = 矩阵A的第m行的所有元素 与 矩阵B的第n列的所有元素 乘积 之和。

注意:

  1. 矩阵A、B相乘,必须满足矩阵A的列数与矩阵B的行数相等,或者矩阵A的行数与矩阵B的列数相等
  2. 矩阵相乘不满足交换律,即AB ≠ BA

色彩处理

在色彩效果处理中,通常会使用以下3个属性来描述一个图像:

  • 色调——物理传播的颜色
  • 饱和度——颜色的纯度,从0 ~ 100% 来描述,即灰白到饱和
  • 亮度——颜色相对明暗程度

这里,是使用ColorMatrix来处理色彩效果,那么可知,该类是有关于以上3个属性的。

实际上,ColorMatrix 是一个4*5的矩阵,但在android中,它是使用一维数组的形式保存的,具体转换交个android自己处理就可以了。

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

图片是由一个个像素组成的,每个像素多有一个颜色矩阵来保存颜色的RGBA值,其与目标图片的[R,G,B,A]阶乘:

图片描述

最终结果是:

   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;

在默认的情况下,ColorMatrix的矩阵是这样的:

  [ 1 0 0 0 0   - red vector
    0 1 0 0 0   - green vector
    0 0 1 0 0   - blue vector
    0 0 0 1 0 ] - alpha vector

注意这里的顺序是:R、G、B、A

问题:色彩一般就是R、G、B、A,4个颜色通道(如图),为什么需要4×5阶乘的矩阵呢?

图片描述

通过上面的计算计算结果,可以看到R’、G’、B’、A’最终的结果后面分别添加了e、j、o、t,这个就是添加的一阶。其作用是对颜色的偏移量。前面的四阶可以对色彩的每个分量进行乘/除运算(即对亮度计算),后面一阶是进行加/减 运算,(即对饱和度计算),比如只对G值单独增加色彩。

使用

通过上面的介绍,我们知道处理色彩,使用ColorMatrix就可以。但对于ColorMatrix的操作,也可以分为2中:

  1. 直接操作ColorMatrix数组值,用于简单的操作,或者是一些固定的数组值;
  2. 通过ColorMatrix的API处理,简单方便,对于复杂的处理不用自己计算;

直接修改ColorMatrix 数组值

1.当创建一个ColorMatrix对象时,或者reset()重置,默认的矩阵如下,即保留原来的图片色彩

/**
* 默认
*/
mColorMatrix = new ColorMatrix(new float[]{
        1,0,0,0,0,
        0,1,0,0,0,
        0,0,1,0,0,
        0,0,0,1,0,
});

2.只保留红色通道,即只保留图片的红色

/**
 * 红色通道
 */
mColorMatrix = new ColorMatrix(new float[]{
        1,0,0,0,0,
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,0,1,0,
});

3.只保留绿色通道

/**
 * 绿色通道
 */
mColorMatrix = new ColorMatrix(new float[]{
        0,0,0,0,0,
        0,1,0,0,0,
        0,0,0,0,0,
        0,0,0,1,0,
});

4.只保留蓝色通道

/**
 * 蓝色通道
 */
mColorMatrix = new ColorMatrix(new float[]{
        0,0,0,0,0,
        0,0,0,0,0,
        0,0,1,0,0,
        0,0,0,1,0,
});

5.改变色彩透明度

/**
 * 色彩透明度
 */
mColorMatrix = new ColorMatrix(new float[]{
        1,0,0,0,0,
        0,1,0,0,0,
        0,0,1,0,0,
        0,0,0,0.5f,0,
});

6.改变色彩偏移量,即饱和度

这里改变绿色的偏移量,增加80。那么每个像素G值多会增加80。

/**
 * 色彩偏移(饱和度)
 */
mColorMatrix = new ColorMatrix(new float[]{
        1,0,0,0,0,
        0,1,0,0,80,
        0,0,1,0,0,
        0,0,0,1,0,
});

7.改变色彩缩放量,即亮度

这里对角线的值增加1.3倍,那么最终颜色值会比原来亮很多。如果对角线的RGB值,取0,那么什么也不显示,那么就是黑色。

/**
 * 色彩缩放(亮度)
 */
mColorMatrix = new ColorMatrix(new float[]{
        1.3f,0,0,0,0,
        0,1.3f,0,0,0,
        0,0,1.3f,0,0,
        0,0,0,1,0,
});

8.改变色彩旋转角度,即色调

这个理解起来有点难度。首先要理解什么是向量,分量。应该多学过,这里给个图看看,不懂的自行了解。对了,还需要三角函数的一点知识。

图片描述

这里将R、G、B作为三维坐标的轴。如图:

图片描述

一般旋转,指的是按照R、G、B的其中一个轴来旋转。
比如,按照B轴来旋转,那么B的颜色值不会改变,R和G的值则会改变。如图:

图片描述

旋转之后,需要重新计算颜色值,那么就需要对R和G颜色向量先进行分解,然后在计算。如图:

图片描述

可以知道,最后计算结果是:

R = G’sin(a) + R’cos(a)
G = G’cos(a) - R’sin(a)

如果是默认的矩阵进行以上的旋转,那么最终的结果是:

图片描述

假设旋转的角度是a = 30度,那么最终的数组是:

mColorMatrix = new ColorMatrix(new float[]{
        0.866f,0.5f,0,0,0,
        -0.5f,0.866f,0,0,0,
        0,0,1.3f,0,0,
        0,0,0,1,0,
});

围绕其他颜色轴的分析也是如此。是不是觉得过于复杂了?我就是想旋转一下而已,给我说这么一大堆…

好吧,装逼过头了,其实我不太懂其中详细的算法(打脸了…)。

没错,android自然给我们提供了API方法,直接调用就可以了。下面会介绍,以上大致说下计算原理,了解一下,方便后面API的理解。

以上所有的效果图:(真是美丽)

图片描述

ColorMatrix 类的使用

如果理解了上面一些计算,那么下面API的介绍就简单了,多是对应的。

构造函数:

ColorMatrix()
如果不配合使用set(), 则使用默认的矩阵

ColorMatrix(float[] src)
使用指定的矩阵src

ColorMatrix(ColorMatrix src)
使用src,创建新的对象

set(float[] src)

设置数组矩阵

set(ColorMatrix src)

将src对象复制给当前对象

reset()

重置,matrix则恢复默认值

setSaturation(float sat)

设置R G B整体的饱和度的放大倍数。

参数:
sat :表示把当前色彩整体饱和度的放大倍数。
取值为0:表示完全无色彩,即灰度图像(黑白图像);
取值为1:表示色彩不变动;
取值大于1:显示色彩过度饱和

使用:

    mColorMatrix.reset();
    mColorMatrix.setSaturation(5);
    mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
    canvas.drawBitmap(mBitmap, 0, 0, mPaint );

setScale(float rScale, float gScale, float bScale, float aScale)

设置缩放来改变亮度,可以分别对R、G、B、A操作。

参数:

4个参数,分别对应R、G、B、A的缩放倍数。(以下测试不考虑透明度)

当值全为0:那么最终颜色000000,则是黑色;
当值全为1:表示色彩不变动;
当值全大于1:亮度增强,足够大时,最终颜色为ffffff,则是白色;

使用:

    mColorMatrix.reset();
    float scale = 1.2f;
    mColorMatrix.setScale(scale, scale, scale, 1);
    mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
    canvas.drawBitmap(mBitmap, 0, 0, mPaint );

setRotate(int axis, float degrees)

设置旋转,来改变色调。具体算法可以参考源码,或者参考上面介绍,可以验证下是否一致。

参数:

axis:表示围绕哪个颜色轴旋转,其值有3个
0:代表围绕 R 旋转
1:代表围绕 G 旋转
2:代表围绕 B 旋转

degrees:旋转的角度值

使用:

    mColorMatrix.reset();
    mColorMatrix.setRotate(1,30);  //和前面直接使用数组,效果是一样的
    mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix));
    canvas.drawBitmap(mBitmap, 0, 0, mPaint );

getArray()

获取当前矩阵数值

对于色彩的变化,也不是很专业的。前面的方法,在我的范围内,已经够用了。以下方法很少用到,需要熟练知道阶乘的运算,还有对色彩的变化有一定的认识,即色彩结果值是不是你想要的结果。这里简单提一下这些方法。

为了更好说明介绍,假设当前的ColorMatrix对象是 colorMatrix

setConcat(ColorMatrix matA, ColorMatrix matB)

阶乘不满足交换律,那么按照阶乘运算规则:matA * matB,将最终结果赋值给当前对象的colorMatrix;

preConcat(ColorMatrix prematrix)

运算规则是:colorMatrix * prematrix,相当于调用 setConcat(this, prematrix);

postConcat(ColorMatrix postmatrix)

运算规则是:postmatrix 8 colorMatrix , 相当于调用 setConcat(postmatrix, this);

setRGB2YUV()

setYUV2RGB()

这2个方法主要是用于切换图像格式

由于在不同的应用领域中为了更好更准确的满足各自的需求,就出现了各种各样的色彩空间模型来量化的描述颜色。比较常接触到的就包括RGB/CMYK/YIQ/YUV/HSI等等。对于数字电子多媒体领域来说,我们经常接触到的色彩空间的概念,主要是RGB,YUV这两种。
RGB是按三基色加光系统的原理来描述颜色,而YUV则是按照亮度,色差的原理来描述颜色

所以,为了保证兼容性,需要进行RGB与YUV格式的互转。

关于色彩格式,具体可以参考这里:http://blog.chinaunix.net/uid-9068997-id-2010406.html

常用的特定效果

灰白效果

与setSaturation(0)效果是一致的,可以参看下源码。

    mColorMatrix = new ColorMatrix(new float[]{
            0.33f,0.59f,0.11f,0,0,
            0.33f,0.59f,0.11f,0,0,
            0.33f,0.59f,0.11f,0,0,
            0.33f,0.59f,0.11f,0,0,
    });

反转效果

    /**
     *反转效果
     */
    mColorMatrix = new ColorMatrix(new float[]{
            -1,0,0,0,255,
            0,-1,0,0,255,
            0,0,-1,0,255,
            0,0,0,1,0,
    });

怀旧效果

    /**
     * 怀旧效果
     */
    mColorMatrix = new ColorMatrix(new float[]{
            1/2f,1/2f,1/2f,0,0,
            1/3f,1/3f,1/3f,0,0,
            1/4f,1/4f,1/4f,0,0,
            0,0,0,1,0,
    });

去色效果

    /**
     * 去色效果
     */
    mColorMatrix = new ColorMatrix(new float[]{
            1.5f,1.5f,1.5f,0,-1,
            1.5f,1.5f,1.5f,0,-1,
            1.5f,1.5f,1.5f,0,-1,
            0,0,0,1,0,
    });

反色效果

    /**
     * 反色效果 (红 绿交换)
     */
    mColorMatrix = new ColorMatrix(new float[]{
            0,1,0,0,0,
            1,0,0,0,0,
            0,0,1,0,0,
            0,0,0,1,0,
    });

以上的效果图:

图片描述

结束

嗯,终于,对于ColorMatrix有了一定的了解。一番总结之后,其实也并没有那么难(抛弃色彩算法)。

至此,再次遇见,是那么熟悉~

主要参考:

https://blog.csdn.net/harvic880925/article/details/51187277

《android群英传》


欢迎点赞

猜你喜欢

转载自blog.csdn.net/pzm1993/article/details/81217861