Понимание использования ColorMatrix и ColorMatrixColorFilter для изменения оттенка чертежа

Я работаю над пользовательским интерфейсом для приложения, и я пытаюсь использовать значки оттенков серого и разрешить пользователю изменять тему на цвет по своему выбору. Для этого я пытаюсь просто применить какой-то цветофильтр, чтобы наложить цвет поверх drawable. Я пробовал использовать PorterDuff.Режим.Умножьте, и он работает почти точно так, как мне нужно, за исключением того, что белые также накладываются на цвет. То, что я идеально ищу, - это что-то вроде режима "цветного" смешивания в Photoshop, где графика сохраняет свою прозрачность и яркость, и только изменяет цвет изображения. Например:

alt text становится alt text

после некоторых исследований кажется, что класс ColorMatrixColorFilter может делать то, что мне нужно, но я не могу найти никаких ресурсов, указывающих на то, как используется матрица. Это матрица 4x5, но мне нужно знать, как я разрабатываю матрицу. Есть идеи?

EDIT: так хорошо, что я нашел до сих пор на этом, как следует:

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? Если это так, я мог бы просто умножить матрицу для этого разговора, а затем сделать настройка оттенка для этой матрицы, а затем применить эту матрицу в качестве цветофильтра.

9 ответов


это то, что я использую для моей игры. Это компиляция различных частей, найденных на различных статьях на веб-сайтах. Кредиты идут оригинальному автору из ссылок @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

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

для всех, кто заинтересован в том, как использовать 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);
}

нет линейной зависимости между оттенком и 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;
}

ниже класс является улучшением по сравнению с ответами, которые уже были опубликованы. Это облегчает чтение и создание ColorFilter С Bitmap.

пример использования:

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

}

Я думаю, что этот метод даст вам то, что вы хотите:

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

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

Как и остальные ответы, я использовал цветовую матрицу для реализации этого поведения, но вы можете передать обычный цветной ресурс 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();
}

Я сделал небольшой тестер ColorMatrixFilter, основанный на следующем фрагменте:

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


даже если много полезных эффектов может быть достигнуто с помощью ColorMatrix Я лично рассмотрю возможность использования ColorMap[] вместе с ImageAttributes. Делая это, мы можем определить, какие цвета должны быть заменены на какие цвета.