使用 ColorMatrix 和 ColorMatrix ColorFilter 修改可绘制色调

我正在为一个应用程序开发一个 UI,我正在尝试使用灰度图标,并允许用户将主题更改为自己选择的颜色。为了做到这一点,我只是试图应用一个颜色过滤器的一些排序覆盖在可绘制的顶部的颜色。我试过用 PorterDuff。多重模式,它的工作几乎完全符合我的需要,除了白色得到覆盖的颜色以及。我想要的是类似 Photoshop 中的“颜色”混合模式,图形保持其透明度和亮度,只修改图像的颜色。例如:

alt text在做了一些研究之后,似乎 ColorMatrixColorFilter 类可以满足我的需要,但是我似乎找不到任何指向矩阵如何使用的资源。这是一个4x5的矩阵,但我需要知道的是我如何去设计矩阵。有什么想法吗?

编辑: 好的,我目前的发现如下:

1 0 0 0 0 //red
0 1 0 0 0 //green
0 0 1 0 0 //blue
0 0 0 1 0 //alpha

其中这个矩阵是恒等矩阵(当应用时,不做任何更改) ,数字范围从0到1(浮点数)。此矩阵将与每个像素相乘以转换为新颜色。所以我开始有点糊涂了。因此,我认为每个像素都是一个1 x 4的矢量,其中包含的 argb 值(例如 0.2, 0.5, 0.8, 1)将点缀着变换矩阵。因此,要将图像的红色强度加倍,你可以使用一个矩阵,比如:

2 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0

它会给你一个 0.4, 0.5, 0.8, 1的矢量(颜色)。从有限的测试来看,情况似乎是这样的,并且工作得很好,但是我实际上还是遇到了同样的问题(比如白色会变色)。进一步的阅读告诉我,这是因为它在 RGB 值上进行转换,而对于色相移动,值应该首先转换为 HSL 值。因此,我可以编写一个类来读取图像并转换颜色,然后用新的颜色重新绘制图像。这给 StateListDrawables 带来了另一个问题,因为我不确定如何在代码中获取并修改所有这些内容,以及这个过程会有多慢。:/

嗯,好的,那么我想我还有一个问题,那就是矩阵是否可以用来将 RGB 转换成另一个带有亮度信息的颜色空间,比如 Lb 或 HSL?如果是这样的话,我可以只是乘以矩阵的转换,然后使色相调整到该矩阵,然后应用该矩阵作为颜色过滤器。

62124 次浏览

色调和 RGB 之间没有线性关系。色相是在60 ° 块(http://en.wikipedia.org/wiki/HSL_color_space#General_approach)中分段定义的,因此 HSV 和 RGB 之间不存在简单的矩阵转换。 To change the hue of an image, you can use the following method :

public Bitmap changeHue( Bitmap source, double hue ) {
Bitmap result = Bitmap.createBitmap( source.getWidth(), source.getHeight(), source.getConfig() );


float[] hsv = new float[3];
for( int x = 0; x < source.getWidth(); x++ ) {
for( int y = 0; y < source.getHeight(); y++ ) {
int c = source.getPixel( x, y );
Color.colorToHSV( c, hsv );
hsv[0] = (float) ((hsv[0] + 360 * hue) % 360);
c = (Color.HSVToColor( hsv ) & 0x00ffffff) | (c & 0xff000000);
result.setPixel( x, y, c );
}
}


return result;
}

对于任何人谁是对如何使用 ColorMatrixColorFilter 感兴趣。我在这里使用的示例,当我在画布上绘制位图时,将每个像素转换为红色。

课堂上的评论来自于: http://developer.android.com/reference/android/graphics/ColorMatrix.html,它给你们一些关于这是如何工作的见解

@Override
protected void onDraw(Canvas canvas) {


// The matrix is stored in a single array, and its treated as follows: [ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]
// When applied to a color [r, g, b, a], the resulting color is computed as (after clamping) ;
//   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;


Paint paint = new Paint();
float[] matrix = {
1, 1, 1, 1, 1, //red
0, 0, 0, 0, 0, //green
0, 0, 0, 0, 0, //blue
1, 1, 1, 1, 1 //alpha
};
paint.setColorFilter(new ColorMatrixColorFilter(matrix));


Rect source = new Rect(0, 0, 100, 100);
Rect dest = new Rect(0, 0, 100, 100);


Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.sampleimage);
canvas.drawBitmap(bitmap , source, dest, paint);
}

This is what I use for my game. This is the compilation of various part found on various articles on websites. Credits goes to the original author from the @see links. Note that a lot more can be done with color matrices. Including inverting, etc...

public class ColorFilterGenerator
{
/**
* Creates a HUE ajustment ColorFilter
* @see http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953
* @see http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
* @param value degrees to shift the hue.
* @return
*/
public static ColorFilter adjustHue( float value )
{
ColorMatrix cm = new ColorMatrix();


adjustHue(cm, value);


return new ColorMatrixColorFilter(cm);
}


/**
* @see http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953
* @see http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
* @param cm
* @param value
*/
public static void adjustHue(ColorMatrix cm, float value)
{
value = cleanValue(value, 180f) / 180f * (float) Math.PI;
if (value == 0)
{
return;
}
float cosVal = (float) Math.cos(value);
float sinVal = (float) Math.sin(value);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]
{
lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143f), lumG + cosVal * (1 - lumG) + sinVal * (0.140f), lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0,
0f, 0f, 0f, 1f, 0f,
0f, 0f, 0f, 0f, 1f };
cm.postConcat(new ColorMatrix(mat));
}


protected static float cleanValue(float p_val, float p_limit)
{
return Math.min(p_limit, Math.max(-p_limit, p_val));
}
}

为了完成这个任务,我应该添加一个示例:

ImageView Sun = (ImageView)findViewById(R.id.sun);
Sun.setColorFilter(ColorFilterGenerator.adjustHue(162)); // 162 degree rotation

我认为这种方法会给你你想要的:

Http://android.okhelp.cz/hue-color-colored-filter-bitmap-image-android-example/

bitmapOrg.setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);

基于以下片段,我做了一个小小的 ColorMatrix Filter 测试器:

private Bitmap setColorFilter(Bitmap drawable) {
Bitmap grayscale  = Bitmap.createBitmap(drawable.getWidth(), drawable.getHeight(), drawable.getConfig());
//if(isRenderMode) bOriginal.recycle();
Canvas c = new Canvas(grayscale );
Paint p = new Paint();


final ColorMatrix matrixA = new ColorMatrix();
matrixA.setSaturation(sauturationValue/2);




float[] mx = {
r1Value,  r2Value,  r3Value,  r4Value,  r5Value,
g1Value,  g2Value,  g3Value,  g4Value,  g5Value,
b1Value,  b2Value,  b3Value,  b4Value,  b5Value,
a1Value,  a2Value,  a3Value,  a4Value,  a5Value
};
final ColorMatrix matrixB = new ColorMatrix(mx);


matrixA.setConcat(matrixB, matrixA);


final ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrixA);
p.setColorFilter(filter);
c.drawBitmap(drawable, 0, 0, p);
return grayscale;
}

你可以在这里查看: Https://play.google.com/store/apps/details?id=org.vaelostudio.colormatrixtester

这里是完整的代码,如果你想调整明亮,对比度,饱和度和色调。享受! 非常感谢@RichardLalancette

public class ColorFilterGenerator {


private static double DELTA_INDEX[] = {
0,    0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1,  0.11,
0.12, 0.14, 0.15, 0.16, 0.17, 0.18, 0.20, 0.21, 0.22, 0.24,
0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42,
0.44, 0.46, 0.48, 0.5,  0.53, 0.56, 0.59, 0.62, 0.65, 0.68,
0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89, 0.92, 0.95, 0.98,
1.0,  1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54,
1.60, 1.66, 1.72, 1.78, 1.84, 1.90, 1.96, 2.0,  2.12, 2.25,
2.37, 2.50, 2.62, 2.75, 2.87, 3.0,  3.2,  3.4,  3.6,  3.8,
4.0,  4.3,  4.7,  4.9,  5.0,  5.5,  6.0,  6.5,  6.8,  7.0,
7.3,  7.5,  7.8,  8.0,  8.4,  8.7,  9.0,  9.4,  9.6,  9.8,
10.0
};


/**
* @see http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953
* @see http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
* @param cm
* @param value
*/
public static void adjustHue(ColorMatrix cm, float value)
{
value = cleanValue(value, 180f) / 180f * (float) Math.PI;
if (value == 0){
return;
}


float cosVal = (float) Math.cos(value);
float sinVal = (float) Math.sin(value);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]
{
lumR + cosVal * (1 - lumR) + sinVal * (-lumR), lumG + cosVal * (-lumG) + sinVal * (-lumG), lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143f), lumG + cosVal * (1 - lumG) + sinVal * (0.140f), lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)), lumG + cosVal * (-lumG) + sinVal * (lumG), lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0,
0f, 0f, 0f, 1f, 0f,
0f, 0f, 0f, 0f, 1f };
cm.postConcat(new ColorMatrix(mat));
}


public static void adjustBrightness(ColorMatrix cm, float value) {
value = cleanValue(value,100);
if (value == 0) {
return;
}


float[] mat = new float[]
{
1,0,0,0,value,
0,1,0,0,value,
0,0,1,0,value,
0,0,0,1,0,
0,0,0,0,1
};
cm.postConcat(new ColorMatrix(mat));
}


public static void adjustContrast(ColorMatrix cm, int value) {
value = (int)cleanValue(value,100);
if (value == 0) {
return;
}
float x;
if (value < 0) {
x = 127 + (float) value / 100*127;
} else {
x = value % 1;
if (x == 0) {
x = (float)DELTA_INDEX[value];
} else {
//x = DELTA_INDEX[(p_val<<0)]; // this is how the IDE does it.
x = (float)DELTA_INDEX[(value<<0)]*(1-x) + (float)DELTA_INDEX[(value<<0)+1] * x; // use linear interpolation for more granularity.
}
x = x*127+127;
}


float[] mat = new float[]
{
x/127,0,0,0, 0.5f*(127-x),
0,x/127,0,0, 0.5f*(127-x),
0,0,x/127,0, 0.5f*(127-x),
0,0,0,1,0,
0,0,0,0,1
};
cm.postConcat(new ColorMatrix(mat));


}


public static void adjustSaturation(ColorMatrix cm, float value) {
value = cleanValue(value,100);
if (value == 0) {
return;
}


float x = 1+((value > 0) ? 3 * value / 100 : value / 100);
float lumR = 0.3086f;
float lumG = 0.6094f;
float lumB = 0.0820f;


float[] mat = new float[]
{
lumR*(1-x)+x,lumG*(1-x),lumB*(1-x),0,0,
lumR*(1-x),lumG*(1-x)+x,lumB*(1-x),0,0,
lumR*(1-x),lumG*(1-x),lumB*(1-x)+x,0,0,
0,0,0,1,0,
0,0,0,0,1
};
cm.postConcat(new ColorMatrix(mat));
}






protected static float cleanValue(float p_val, float p_limit)
{
return Math.min(p_limit, Math.max(-p_limit, p_val));
}


public static ColorFilter adjustColor(int brightness, int contrast, int saturation, int hue){
ColorMatrix cm = new ColorMatrix();
adjustHue(cm, hue);
adjustContrast(cm, contrast);
adjustBrightness(cm, brightness);
adjustSaturation(cm, saturation);


return new ColorMatrixColorFilter(cm);
}
}

尽管通过使用 ColorMatrix可以获得很多有用的效果,我个人还是会考虑使用 ColorMap[]ImageAttributes。通过这样做,我们可以定义哪些颜色应该被替换为哪些颜色。

下面的课程是对已经发布的答案的一个改进。这使得从 Bitmap读取和创建 ColorFilter变得更加容易。

示例用法:

ImageView imageView = ...;
Drawable drawable = imageView.getDrawable();
ColorFilter colorFilter = ColorFilterGenerator.from(drawable).to(Color.RED);
imageView.setColorFilter(colorFilter);

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PictureDrawable;
import android.widget.ImageView;


/**
* Creates a {@link ColorMatrixColorFilter} to adjust the hue, saturation, brightness, or
* contrast of an {@link Bitmap}, {@link Drawable}, or {@link ImageView}.
* <p/>
* Example usage:
* <br/>
* {@code imageView.setColorFilter(ColorFilterGenerator.from(Color.BLUE).to(Color.RED));}
*
* @author Jared Rummler <jared.rummler@gmail.com>
*/
public class ColorFilterGenerator {


// Based off answer from StackOverflow
// See: http://stackoverflow.com/a/15119089/1048340


private ColorFilterGenerator() {
throw new AssertionError();
}


public static From from(Drawable drawable) {
return new From(drawableToBitmap(drawable));
}


public static From from(Bitmap bitmap) {
return new From(bitmap);
}


public static From from(int color) {
return new From(color);
}


// --------------------------------------------------------------------------------------------


private static final double DELTA_INDEX[] = {
0, 0.01, 0.02, 0.04, 0.05, 0.06, 0.07, 0.08, 0.1, 0.11, 0.12, 0.14, 0.15, 0.16, 0.17, 0.18,
0.20, 0.21, 0.22, 0.24, 0.25, 0.27, 0.28, 0.30, 0.32, 0.34, 0.36, 0.38, 0.40, 0.42, 0.44,
0.46, 0.48, 0.5, 0.53, 0.56, 0.59, 0.62, 0.65, 0.68, 0.71, 0.74, 0.77, 0.80, 0.83, 0.86, 0.89,
0.92, 0.95, 0.98, 1.0, 1.06, 1.12, 1.18, 1.24, 1.30, 1.36, 1.42, 1.48, 1.54, 1.60, 1.66, 1.72,
1.78, 1.84, 1.90, 1.96, 2.0, 2.12, 2.25, 2.37, 2.50, 2.62, 2.75, 2.87, 3.0, 3.2, 3.4, 3.6,
3.8, 4.0, 4.3, 4.7, 4.9, 5.0, 5.5, 6.0, 6.5, 6.8, 7.0, 7.3, 7.5, 7.8, 8.0, 8.4, 8.7, 9.0, 9.4,
9.6, 9.8, 10.0
};


public static void adjustHue(ColorMatrix cm, float value) {
value = cleanValue(value, 180f) / 180f * (float) Math.PI;
if (value == 0) {
return;
}


float cosVal = (float) Math.cos(value);
float sinVal = (float) Math.sin(value);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]{
lumR + cosVal * (1 - lumR) + sinVal * (-lumR),
lumG + cosVal * (-lumG) + sinVal * (-lumG),
lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143f),
lumG + cosVal * (1 - lumG) + sinVal * (0.140f),
lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)),
lumG + cosVal * (-lumG) + sinVal * (lumG),
lumB + cosVal * (1 - lumB) + sinVal * (lumB), 0, 0, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f,
0f, 1f
};
cm.postConcat(new ColorMatrix(mat));
}


public static void adjustBrightness(ColorMatrix cm, float value) {
value = cleanValue(value, 100);
if (value == 0) {
return;
}


float[] mat = new float[]{
1, 0, 0, 0, value, 0, 1, 0, 0, value, 0, 0, 1, 0, value, 0, 0, 0, 1, 0, 0, 0, 0, 0,
1
};
cm.postConcat(new ColorMatrix(mat));
}


public static void adjustContrast(ColorMatrix cm, int value) {
value = (int) cleanValue(value, 100);
if (value == 0) {
return;
}
float x;
if (value < 0) {
x = 127 + value / 100 * 127;
} else {
x = value % 1;
if (x == 0) {
x = (float) DELTA_INDEX[value];
} else {
x = (float) DELTA_INDEX[(value << 0)] * (1 - x)
+ (float) DELTA_INDEX[(value << 0) + 1] * x;
}
x = x * 127 + 127;
}


float[] mat = new float[]{
x / 127, 0, 0, 0, 0.5f * (127 - x), 0, x / 127, 0, 0, 0.5f * (127 - x), 0, 0,
x / 127, 0, 0.5f * (127 - x), 0, 0, 0, 1, 0, 0, 0, 0, 0, 1
};
cm.postConcat(new ColorMatrix(mat));


}


public static void adjustSaturation(ColorMatrix cm, float value) {
value = cleanValue(value, 100);
if (value == 0) {
return;
}


float x = 1 + ((value > 0) ? 3 * value / 100 : value / 100);
float lumR = 0.3086f;
float lumG = 0.6094f;
float lumB = 0.0820f;


float[] mat = new float[]{
lumR * (1 - x) + x, lumG * (1 - x), lumB * (1 - x), 0, 0, lumR * (1 - x),
lumG * (1 - x) + x, lumB * (1 - x), 0, 0, lumR * (1 - x), lumG * (1 - x),
lumB * (1 - x) + x, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1
};
cm.postConcat(new ColorMatrix(mat));
}


// --------------------------------------------------------------------------------------------


private static float cleanValue(float p_val, float p_limit) {
return Math.min(p_limit, Math.max(-p_limit, p_val));
}


private static float[] getHsv(int color) {
float[] hsv = new float[3];
Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), hsv);
return hsv;
}


/**
* Converts a {@link Drawable} to a {@link Bitmap}
*
* @param drawable
*     The {@link Drawable} to convert
* @return The converted {@link Bitmap}.
*/
private static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
} else if (drawable instanceof PictureDrawable) {
PictureDrawable pictureDrawable = (PictureDrawable) drawable;
Bitmap bitmap = Bitmap.createBitmap(pictureDrawable.getIntrinsicWidth(),
pictureDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawPicture(pictureDrawable.getPicture());
return bitmap;
}
int width = drawable.getIntrinsicWidth();
width = width > 0 ? width : 1;
int height = drawable.getIntrinsicHeight();
height = height > 0 ? height : 1;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
}


/**
* Calculate the average red, green, blue color values of a bitmap
*
* @param bitmap
*     a {@link Bitmap}
* @return
*/
private static int[] getAverageColorRGB(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int size = width * height;
int[] pixels = new int[size];
int r, g, b;
r = g = b = 0;
bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
for (int i = 0; i < size; i++) {
int pixelColor = pixels[i];
if (pixelColor == Color.TRANSPARENT) {
size--;
continue;
}
r += Color.red(pixelColor);
g += Color.green(pixelColor);
b += Color.blue(pixelColor);
}
r /= size;
g /= size;
b /= size;
return new int[]{
r, g, b
};
}


/**
* Calculate the average color value of a bitmap
*
* @param bitmap
*     a {@link Bitmap}
* @return
*/
private static int getAverageColor(Bitmap bitmap) {
int[] rgb = getAverageColorRGB(bitmap);
return Color.argb(255, rgb[0], rgb[1], rgb[2]);
}


// Builder
// --------------------------------------------------------------------------------------------


public static final class Builder {


int hue;


int contrast;


int brightness;


int saturation;


public Builder setHue(int hue) {
this.hue = hue;
return this;
}


public Builder setContrast(int contrast) {
this.contrast = contrast;
return this;
}


public Builder setBrightness(int brightness) {
this.brightness = brightness;
return this;
}


public Builder setSaturation(int saturation) {
this.saturation = saturation;
return this;
}


public ColorFilter build() {
ColorMatrix cm = new ColorMatrix();
adjustHue(cm, hue);
adjustContrast(cm, contrast);
adjustBrightness(cm, brightness);
adjustSaturation(cm, saturation);
return new ColorMatrixColorFilter(cm);
}
}


public static final class From {


final int oldColor;


private From(Bitmap bitmap) {
oldColor = getAverageColor(bitmap);
}


private From(int oldColor) {
this.oldColor = oldColor;
}


public ColorFilter to(int newColor) {
float[] hsv1 = getHsv(oldColor);
float[] hsv2 = getHsv(newColor);
int hue = (int) (hsv2[0] - hsv1[0]);
int saturation = (int) (hsv2[1] - hsv1[1]);
int brightness = (int) (hsv2[2] - hsv1[2]);
return new ColorFilterGenerator.Builder()
.setHue(hue)
.setSaturation(saturation)
.setBrightness(brightness)
.build();
}
}


}

像其他的答案一样,我使用颜色矩阵来实现这个行为,但是你可以传递一个常规的 Android 颜色资源。矩阵,将颜色映射到图像值和白色之间的范围。

/**
* Color everything that isn't white, the tint color
* @param tintColor the color to tint the icon
*/
public void setInverseMultiplyFilter(Drawable imgCopy, @ColorInt int tintColor) {


Drawable imgCopy = imageView.getDrawable().getConstantState().newDrawable();


float colorRed = Color.red(tintColor) / 255f;
float colorGreen = Color.green(tintColor) / 255f;
float colorBlue = Color.blue(tintColor) / 255f;


imgCopy.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[]{
1 - colorRed, 0,              0,             0,     colorRed * 255,
0,            1 - colorGreen, 0,             0,     colorGreen * 255,
0,            0,              1 - colorBlue, 0,     colorBlue * 255,
0,            0,              0,             Color.alpha(tintColor) / 255f, 0,
})));


imageView.setImageDrawable(imgCopy);
imageView.invalidate();
}

我解决这个问题的方法是从一个灰度图像开始。

译自: 美国《科学》杂志网站(https://i.stack.imgur.com/rfDkz.png)

你可以很容易地在 Photoshop 中完成,或者如果你需要用代码来完成,你可以使用以下方法。

private fun changeImageToGreyScale() {
val matrix = ColorMatrix()
matrix.setSaturation(0f)


val originalBitmap = BitmapFactory.decodeResource(resources, originalImageID)
val bitmapCopy = Bitmap.createBitmap(originalBitmap.width,
originalBitmap.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmapCopy)


val paint = Paint()
val colorFilter = ColorMatrixColorFilter(matrix)
paint.colorFilter = colorFilter
canvas.drawBitmap(originalBitmap, 0f, 0f, paint)
grayScaleImage = bitmapCopy
}

获得 GrayScale 图像后,可以使用 PorterDuff OVERLAY 模式添加所需的 Color。

private fun applyFilterToImage() {


val bitmapCopy = Bitmap.createBitmap(grayScaleImage.width, grayScaleImage.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmapCopy)


val rnd = java.util.Random()
val randomColor = Color.rgb(rnd.nextInt(256), rnd.nextInt(256), rnd.nextInt(256))


val paint = Paint()
val porterDuffMode = PorterDuff.Mode.OVERLAY
paint.colorFilter = PorterDuffColorFilter(randomColor, porterDuffMode)


val maskPaint = Paint()
maskPaint. xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_ATOP)


canvas.drawBitmap(grayScaleImage, 0f, 0f, paint)


/**
Note OVERLAY will disregard the alpha channel and will turn transparent
pixels into the randomColor. To resolve this I clip out that transparent area by
drawing the image again with the DST_ATOP XferMode
**/
canvas.drawBitmap(grayScaleImage, 0f, 0f, maskPaint)


imageView.setImageBitmap(bitmapCopy)
}

上述结果如下 ColorGif