了解使用 ColorMatrix 和 ColorMatrixColorFilter 修改 Drawable 的色调

Posted

技术标签:

【中文标题】了解使用 ColorMatrix 和 ColorMatrixColorFilter 修改 Drawable 的色调【英文标题】:Understanding the Use of ColorMatrix and ColorMatrixColorFilter to Modify a Drawable's Hue 【发布时间】:2011-05-20 06:56:37 【问题描述】:

我正在开发应用程序的 UI,我正在尝试使用灰度图标,并允许用户将主题更改为他们选择的颜色。为此,我试图仅应用某种 ColorFilter 来在可绘制对象的顶部覆盖颜色。我试过使用 PorterDuff.Mode.MULTIPLY,它几乎完全符合我的需要,除了白色也会被颜色覆盖。我理想中寻找的是类似于 Photoshop 中的“颜色”混合模式,其中图形保留其透明度和亮度,并且只修改图像的颜色。例如: 变为 经过一番研究,似乎 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 转换为具有亮度信息的另一个颜色空间,例如 Lab 或 HSL?如果是这样,我可以乘以该转换的矩阵,然后对该矩阵进行色调调整,然后将该矩阵应用为 ColorFilter。

【问题讨论】:

我在这个主题上找到的最好的文章在这里:active.tutsplus.com/tutorials/effects/… 【参考方案1】:

这是我在游戏中使用的。这是网站上各种文章中的各个部分的汇编。致谢来自@see 链接的原作者。 请注意,使用颜色矩阵可以完成更多工作。包括反相等……

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

【讨论】:

它确保它不会越界并导致伪影。 @RichardLalancette 你能解释一下为什么你的代码中的矩阵mat 有5 行吗?不应该只有 4 行(每个 R G B A 一个)吗? @RichardLalancette 看了ColorMatrix的源码,收回了。 mat 中的最后一行根本没有被使用。 ColorMatrix 构造函数只取前 4 行(复制给定源矩阵的前 20 个浮点数)。 我只是在没有第5行的情况下测试了它,结果似乎是一样的。 什么是 float lumR = 0.213f;浮动 lumG = 0.715f;浮动 lumB = 0.072f;你为什么选择这些价值观?【参考方案2】:

如果你想调整亮度、对比度、饱和度和色调,这里是完整的代码。享受! 非常感谢@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);


【讨论】:

是否可以仅更改一种颜色(蓝色)的饱和度? “值 感谢您提供有价值的代码。关于您的 adjustContrast 方法的两个 cmets:(1) 表达式 value&lt;&lt;0 似乎是多余的,因为它总是等于(只是)value(2) 由于value 是一个整数,所以value % 1 不总是等于0 吗? 亮度的可能值范围错误。它应该在 -255 和 255 之间或之后转换。【参考方案3】:

适用于对如何使用 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);

【讨论】:

您的代码非常适合我的情况,我需要为一些 PNG 着色:gist.github.com/puelocesar/9710910 - 谢谢!【参考方案4】:

下面的课程是对已经发布的答案的改进。这使得从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 ***
  // See: http://***.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();
    
  


【讨论】:

【参考方案5】:

Hue 和 RGB 之间没有线性关系。色调以 60° 块 (http://en.wikipedia.org/wiki/HSL_color_space#General_approach) 分段定义,因此 HSV 和 RGB 之间没有简单的矩阵转换。 要改变图像的色调,可以使用以下方法:

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;

【讨论】:

【参考方案6】:

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

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

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

【讨论】:

请注意,此方法不会以与上面显示的 ColorFilterGenerator 方法相同的方式更改色调。要查看它可能产生什么,请在 Photoshop 或 GIMP 中打开您的位图。在图像顶部创建一个图层并用您想要的颜色填充它。然后设置overlay的图层模式为multiply。将图层模式设置为色调以预览 ColorFilterGenerator 方法将执行的操作。【参考方案7】:

与其他答案一样,我使用颜色矩阵来实现此行为,但您可以传入常规的 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();

【讨论】:

你好本杰明,通过使用上面的矩阵,我们可以为除了绿色以外的所有东西着色吗?而且我们还可以组合一种以上的颜色来保存吗?例如保留白色和绿色,色调休息。请您的回答对我有帮助【参考方案8】:

我做了一个小小的 ColorMatrixFilter 测试器,基于下面的 sn-p:

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

【讨论】:

【参考方案9】:

尽管使用ColorMatrix 可以实现很多有用的效果,但我个人会考虑将ColorMap[]ImageAttributes 一起使用。通过这样做,我们可以定义哪些颜色应该替换为哪些颜色。

【讨论】:

什么是ColorMap?我认为标准 java/android 库中没有这样的类。【参考方案10】:

我解决它的方法是从灰度图像开始。

original ---> grayImage

您可以在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

获得灰度图像后,您可以使用 PorterDuff OVERLAY 模式添加所需的颜色。

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)

以上结果如下 randomColorGif

【讨论】:

以上是关于了解使用 ColorMatrix 和 ColorMatrixColorFilter 修改 Drawable 的色调的主要内容,如果未能解决你的问题,请参考以下文章

如何将 ColorMatrix 与 Canvas Context 一起应用

怎样才能得到ffmpeg的colorchannelmixer的colormatrix

ColorMatrix图片饱和度设置

Android 颜色矩阵——ColorMatrix

利用GDI+处理图像的色彩

ActionScript 3 AS3 ColorMatrix类