自定义View - ColorMatrix(译)

247 阅读6分钟

[TOC]

关联地址

大致翻译自: Android Image Color Change With ColorMatrix

英语好的小伙伴建议直接看原文,原文底部还有作者关于 PorterDuffLighting Control 的文章。

项目中我放了两张图,替换使用可能会让你理解起来更加轻松。

建议下载原作者 Demo 体验,效果更好!

什么是 ColorMatrix

ColorMatrix 是包含四行五列数值的一维数组。如下:

[ 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;

看起来挺难以理解的,没关系,我们来通过一些例子来简单说明下。

原始颜色

如果想通过原始颜色绘制图像,那么可以使用如下矩阵:

[ 1, 0, 0, 0, 0,  0, 1, 0, 0, 0,  0, 0, 1, 0, 0,  0, 0, 0, 1, 0 ]

为什么呢? 我们带入公式计算一下可得:

R' = 1*R + 0*G + 0*B + 0*A = 1*R
G' = 0*R + 1*G + 0*B + 0*A = 1*G
B' = 0*R + 0*G + 1*B + 0*A = 1*B
A' = 0*R + 0*G + 0*B + 1*A = 1*A

[R'G'B'A'] = [RGBA]

所以通过这样的矩阵对原始颜色进行运算绘制的新的图像和原图像是一模一样的。

着色

如果想要用某一种颜色为图像着色,那么只需要修改颜色矩阵中对应的那一行即可。

以红色为示例。

如果我们想让图像中的色彩只包含红色和无色(无色代表红绿蓝三原色都为空,即指黑色),只需要修改第一行就够了。如下:

[ 1, 1, 1, 0, 0,  0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  0, 0, 0, 1, 0 ]

带入公式计算(假设原图像为透明度为不透明):

R' = 1*R + 1*G + 1*B
G' = 0
B' = 0
A' = 1*A

经过以上色彩矩阵的运算,红、绿、蓝等强色将转变为强红色,弱色将变为弱红色或者是黑 色。效果如下:

这个结论可能会比较难以理解,其实如果将纯绿色或者纯蓝色带入以上公式就比较好理解了。如果图像的某个色值正好是纯绿色 [RGB] = [0,255,0,1],带入以上计算可得如下:

R' = 1*0 + 1*255 + 1*0 = 255
G' = 0                = 0
B' = 0                = 0
A' = 1*A              = A

[R'G'B'A'] = [255,0,0,1]

纯绿色经过颜色矩阵的运算的到了纯红色。其他色值也是一样的。

这是使用红色对图像进行着色,绿色和蓝色也是一样的道理,不同的是绿色要修改的是第二行的数据,蓝色要修改的是第三行的数据而已。这里就不贴图了。

GREEN

[ 0, 0, 0, 0, 0,  1, 1, 1, 0, 0,  0, 0, 0, 0, 0,  0, 0, 0, 1, 0 ]

BLUE

[ 0, 0, 0, 0, 0,  0, 0, 0, 0, 0,  1, 1, 1, 0, 0,  0, 0, 0, 1, 0 ]

灰度

灰色中红、绿、蓝三种颜色的占比是一致的。也就是说色彩中红、绿、蓝三种颜色占比一致就是灰色,也就是或暗或亮的区别而已。

通过如下色彩矩阵可以把原始色彩都变为灰色。

[ 0.33, 0.33, 0.33, 0, 0,  0.33, 0.33, 0.33, 0, 0,  0.33, 0.33, 0.33, 0, 0,  0, 0, 0, 1, 0 ]

这里为什么使用 0.33 呢,你可以试一下数值越接近 0 灰色越暗,直至纯黑色,数值越接近 1 灰色越亮,直至纯白色。

让我们将上面的色彩矩阵带入公式:

R' = 0.33*R + 0.33*G + 0.33*B
G' = 0.33*R + 0.33*G + 0.33*B
B' = 0.33*R + 0.33*G + 0.33*B
A' = 1*A 

也就是说任何一种色彩输入以上运算中,得到的都会是一种或暗或亮的灰色。也就得到了下面这张图:

那如果只想显示原始色彩中红色的部分,并绘制到新图中为灰色怎么办呢,可以使用如下色彩矩阵:

[1,0,0,0,0,
 1,0,0,0,0,
 1,0,0,0,0,
 0,0,0,1,0]

红色的部分会变为灰色,而且越红,灰色越亮,直至纯白色。

相应的如果想灰度绿色部分:

[0,1,0,0,0,
 0,1,0,0,0,
 0,1,0,0,0,
 0,0,0,1,0]

灰度蓝色部分:

[0,0,1,0,0,
 0,0,1,0,0,
 0,0,1,0,0,
 0,0,0,1,0]

反转颜色

如果我们想实现像过去的胶卷一样的图像效果,那我们就需要对色值进行反转,那么也就需要色彩矩阵中一直没有用到的第五列数据了。

先来介绍一个白平衡的概念

不管在任何光源下,都能将白色物体还原为白色,对在特定光源下拍摄时出现的偏色现象,通过加强对应的补色来进行补偿。

而 ColorMatrix 中第五列数据就是用来处理白平衡的。如下:

[ -1, 0, 0, 0, 255,  0, -1, 0, 0, 255,  0, 0, -1, 0, 255,  0, 0, 0, 1, 0 ]

继续带入公式运算:

R' = -1*R + 255 = 255 -1*R
G' = -1*G + 255 = 255 -1*G
B' = -1*B + 255 = 255 -1*B
A' = 1*A

得到新的图像如下:

在开始下面的内容前,先讲解一个重要的知识点:

如果计算的色值大于255,则上限为255。如果计算的色值小于0,则下限为0。

ColorMatirx

Android 为我们为我们提供了简便操作矩阵的方式,使用 ColorMatrix

初始化 ColorMatrix

val colorMatrix = ColorMatrix()

ColorMatrix 中矩阵的初始值为:

[1,0,0,0,0,
 0,1,0,0,0,
 0,0,1,0,0,
 0,0,0,1,0]

使用初始矩阵不会对原图像造成任何修改。

ColorMatrix 使用:

val colorFilter = ColorMatrixColorFilter(colorMatrix)

常用方法

因为没有在实际中使用做以下方法实现某些功能,所以仅做记录。如果有这方面的经验希望能交流一下^_^

setScale 设置色彩亮度
/**
 * 设置色彩亮度
 */
public void setScale(float rScale, float gScale, float bScale,  float aScale)

参数可以传入 0 - 1 的值, 原始颜色会变成 黑色 - 原色 中的某个值。当然也可以传入 >1 的值,原始颜色会变的更亮(直至最亮的值 255)。

setSaturation 设置色彩饱和度
/**
 * 设置色彩饱和度
 *
 * @param sat A value of 0 maps the color to gray-scale. 1 is identity.
 */
public void setSaturation(float sat)

setSaturationsetScale 看起来差不多,但是 setSaturation 不会将色彩变黑或变白。

setRotate 色彩旋转
/**
 * 通过指定的值设置颜色轴上的旋转。
 */
public void setRotate(int axis, float degrees)

setRotate 类似于 setScale,但 setRotate 专门适用于一种全色(或红色,绿色或蓝色)。

  • 参数 colorInt0=red, 1=green, 2=blue
  • 参数 degree 取值范围是 0 to 360,它是原来的颜色的时候 degree=0 or 360,并 degree=180colorInt 对应的纯色。
setRGB2YUV 设置矩阵将RGB转换为YUV
/**
 * 设置矩阵将RGB转换为YUV
 */
public void setRGB2YUV()
setYUV2RGB 设置矩阵将YUV转换为RGB
/**
 * 设置矩阵将YUV转换为RGB
 */
public void setYUV2RGB()