持续创作,加快生长!这是我参与「日新计划 · 10 月更文应战」的第21天,点击查看活动详情

能够经过设置 Paint 的 ColorFilter 做图片的色彩处理

mPaint.setColorFilter(ColorFilter filter);

经过为制作设置一致过滤之后,能够有各色各样的作用

image.png

为制作设置色彩过滤,一般运用 ColorFilter 三个子类

  • LightingColorFilter 光照作用
  • PorterDuffColorFilter 指定一个色彩和一种 PorterDuff.Mode 与制作方针进行组成
  • ColorMatrixColorFilter 习惯一个ColorMatix来对色彩进行处理

LightColorFilter 滤镜

LIghtColorFilter 是用来模拟简略的光照作用的,其结构办法:

/**
 * R' = R * mul.R + add.R
 * G' = G * mul.G + add.G
 * B' = B * mul.B + add.B
 *
 * @param mul 用来和方针像素相乘
 * @param add 用来和方针像素相加
 *
 */
public LightingColorFilter(@ColorInt int mul, @ColorInt int add)

依据上面的核算办法,一个保持原始图片作用 mul 是 0xffffff,add 是 0x000000

R' = R*0xff + 0 *R = R //其他通道同理

假如要去掉赤色,能够修正 mul 为 0x00ffff,便是让 R ‘ = 0

假如想让绿色更亮,能够修正 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);

image.png

PorterDuffColorFilter滤镜

这个滤镜的作用是指定一个色彩和一种 PorterDuff.Mode 与制作方针进行组成。它的结构办法如下:

/**
 * 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) 

结构办法两个参数别离是一个指定的色彩 color 和 指定的混合形式 ProterDuff.Mode.

示例作用

PorterDuffColorFilter porterDuffColorFilter = new PorterDuffColorFilter(Color.RED, PorterDuff.Mode.DARKEN);
mPaint.setColorFilter(porterDuffColorFilter);
canvas.drawBitmap(mBitmap, 100, 100, mPaint);

image.png

能够看出, 前一篇的混合形式是图层和图层进行混合,而此处是色彩和图层混合。

ColorMatrixColorFilter

ColorMatrixColorFilter 能够经过色彩矩阵来处理图形的色彩作用,它有两个结构函数

/**
	* Create a color filter that transforms colors through a 4x5 color matrix.
	*
	* @param array 一维数组表明的4行5列的矩阵数组
	*/
public ColorMatrixColorFilter(@NonNull float[] array)
/**
	* Create a color filter that transforms colors through a 4x5 color matrix.
  *
  * @param matrix 4行5列的矩阵数组
	*/
public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) 

色彩矩阵剖析

Android 是运用一个色彩矩阵 ColorMatrix 来处理图形的色彩作用,关于图像的每个像素点,都有一个色彩重量矩阵的 RGBA 值(下图矩阵C)。Android 中的的色彩矩阵是一个4×5的数字矩阵,用来对图片的色彩进行处理(下图矩阵A),如下

A=[abcdefghijklmnopqrst]C=[RGBA1]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]

假如咱们想要改动一张图像的色彩显示作用。能够用矩阵的乘法运算来修正色彩重量矩阵的值。比如上面的矩阵A,体系会以一位数组(float[] array)形式来存储[a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t],而 C 则是一个色彩矩阵的重量。在处理图像时,运用矩阵乘法运算 AC 来处理色彩重量矩阵,如下:

R=AC=[abcdefghijklmnopqrst][RGBA1]=[aRbGcBdAefRgGhBiAjkRlGmBnAopRqGrBsAt]=[R1G1B1A1]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=[10000010000010000010]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=[100010001001000010000010]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

能够设置不同的矩阵系数就能够使得图片有不同的滤镜作用,就如开篇的图片一样

ColorMatrix

ColorMatrixColorFilter 还有一种结构参数,参数是 ColorMatrix 实例:

/**
	* Create a color filter that transforms colors through a 4x5 color matrix.
  *
  * @param matrix 4行5列的矩阵数组
	*/
public ColorMatrixColorFilter(@NonNull ColorMatrix matrix) 

ColorMatrix 能够设置色彩的亮度、饱和度、色彩。

ColorMatrix cm = new ColorMatrix();
//亮度调理
cm.setScale(1,2,1,1);
//饱和度调理0-无色彩, 1- 默许作用, >1饱和度加强
cm.setSaturation(2);
//色彩调理
cm.setRotate(0, 45);
mColorMatrixColorFilter = new ColorMatrixColorFilter(cm);

别离看下里边的源码,本质上也是改动 4*5 矩阵

亮度

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

可看出该办法实践也是操作初始矩阵的系数来达到相应作用的。

色彩

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=[cosαsinα000cosα−sinα0000010000010]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;
}