Comprender el uso de ColorMatrix y ColorMatrixColorFilter para modificar el tono de un dibujable


114

Estoy trabajando en una interfaz de usuario para una aplicación, y estoy intentando usar íconos en escala de grises y permitir que el usuario cambie el tema a un color de su elección. Para hacer esto, estoy tratando de aplicar un ColorFilter de algún tipo para superponer un color en la parte superior del dibujable. Intenté usar PorterDuff.Mode.MULTIPLY, y funciona casi exactamente como lo necesito, excepto que los blancos también se superponen con el color. Lo que idealmente busco es algo así como el modo de fusión "Color" en Photoshop, donde el gráfico conserva su transparencia y luminosidad, y solo modifica el color de la imagen. Por ejemplo: se

texto alternativoconvierte entexto alternativo

Después de investigar un poco, parece que la clase ColorMatrixColorFilter puede hacer lo que necesito, pero parece que no puedo encontrar ningún recurso que indique cómo se usa la matriz. Es una matriz de 4x5, pero lo que necesito saber es cómo hago para diseñar la matriz. ¿Algunas ideas?

EDITAR: Está bien, lo que he encontrado hasta ahora sobre esto es lo siguiente:

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

Donde esta matriz es la matriz de identidad (cuando se aplica, no hace cambios), y los números van de 0 a 1 (flotantes). Esta matriz se multiplicará con cada píxel para convertir al nuevo color. Así que aquí es donde empieza a ser confuso para mí. Entonces, creo que cada píxel sería un vector de 1 x 4 que contiene los valores de argb (por ejemplo 0.2, 0.5, 0.8, 1) que estarán punteados con la matriz de transformación. Entonces, para duplicar la intensidad del rojo de una imagen, usaría una matriz como:

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

que le daría un vector (color) de 0.4, 0.5, 0.8, 1 . De las pruebas limitadas, este parece ser el caso y funciona correctamente, pero en realidad todavía termino con el mismo problema (es decir, los blancos ganan color). La lectura adicional me dice que esto se debe a que está haciendo la conversión en valores RGB, mientras que para el cambio de tono, los valores primero deben convertirse a valores HSL. Así que posiblemente podría escribir una clase que leería la imagen y convertiría los colores, y volvería a dibujar la imagen con los nuevos colores. Esto crea OTRO problema con StateListDrawables, ya que no estoy seguro de cómo haría para obtener cada uno de estos en el código y modificarlos, y qué tan lento sería el proceso. : /

Hmm, está bien, entonces supongo que otra pregunta que tendría es si se puede usar una matriz para convertir RGB a otro espacio de color con información de luminosidad, como L a b o HSL. Si es así, podría simplemente multiplicar la matriz para esa conversación, luego hacer el ajuste de tono a ESA matriz, luego aplicar esa matriz como el ColorFilter.


5
El mejor artículo que encontré sobre este tema está aquí: active.tutsplus.com/tutorials/effects/…
Richard Lalancette

Respuestas:


77

Esto es lo que uso para mi juego. Esta es la recopilación de varias partes que se encuentran en varios artículos en sitios web. Los créditos van al autor original de los enlaces de @ver. Tenga en cuenta que se puede hacer mucho más con las matrices de colores. Incluyendo invertir, 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));
}
}

Para completar esto debo agregar un ejemplo:

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

4
se asegura de que no se salga del límite y cause artefactos.
Richard Lalancette

1
@RichardLalancette ¿Puede explicar por qué la matriz maten su código tiene 5 filas? ¿No deberían ser solo 4 filas (una para cada RGBA)?
ilomambo

1
@RichardLalancette Después de mirar el código fuente de ColorMatrix, lo retiro . La última fila de matnunca se usa en absoluto. El ColorMatrixconstructor solo toma las primeras 4 filas (copia los primeros 20 flotantes de la matriz fuente dada).
ilomambo

2
Lo probé sin la quinta fila y los resultados parecen ser los mismos.
miguel

2
qué son flotadores lumR = 0.213f; flotador lumG = 0,715f; flotador lumB = 0.072f; y ¿por qué ha elegido esos valores?
Sujay UN

43

aquí está el código completo si desea ajustar el brillo, el contraste, la saturación y el tono. ¡Disfrutar! Muchas gracias a @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);
}
}

¿Está disponible para cambiar la saturación de un solo color (azul)?
filipst

"valor << 0" es lo mismo que "valor"
Amir Uval

Gracias por un valioso código. Dos comentarios con respecto a su adjustContrastmétodo: (1) la expresión value<<0parece redundante, ya que siempre sería igual a (solo) value. (2) Como valuees un número entero, ¿no value % 1sería siempre igual a 0?
Bliss

El rango de valores posibles para el brillo es incorrecto. Debe estar entre -255 y 255 o convertirse posteriormente.
woodii

12

Para cualquiera que esté interesado en cómo usar ColorMatrixColorFilter. La muestra que utilicé aquí, convirtió cada píxel en rojo cuando dibujo el mapa de bits en el lienzo.

El comentario en la clase es de: http://developer.android.com/reference/android/graphics/ColorMatrix.html esto le da algunas ideas sobre cómo está funcionando esto

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

Su código funcionó perfectamente para mi caso, donde necesitaba colorear algunos PNG: gist.github.com/puelocesar/9710910 - ¡Gracias!
Paulo Cesar

9

La siguiente clase es una mejora de las respuestas que ya se han publicado. Esto facilita la lectura y la creación de un ColorFilterarchivo Bitmap.

Uso de ejemplo:

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

}

8

No existe una relación lineal entre Hue y RGB. El tono se define por partes en fragmentos de 60 ° ( http://en.wikipedia.org/wiki/HSL_color_space#General_approach ) y, por lo tanto, no existe una conversión matricial simple entre HSV y RGB. Para cambiar el tono de una imagen, puede utilizar el siguiente método:

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

7

Creo que este método te dará lo que quieres:

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

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

1
Tenga en cuenta que este método no cambiará el tono de la misma manera que los métodos ColorFilterGenerator que se muestran arriba. Para ver lo que puede producir, abra su mapa de bits en Photoshop o GIMP. Crea una capa sobre la imagen y rellénala con el color que desees. Luego configure el modo de capa de la superposición para multiplicar. Configure el modo de capa en tono para obtener una vista previa de lo que harán los métodos ColorFilterGenerator.
Kevin Mark

6

Al igual que el resto de las respuestas, utilicé una matriz de colores para implementar este comportamiento, pero puede pasar un recurso de color de Android normal. La matriz mapea el color en un rango entre el valor de la imagen y el blanco.

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

Hola Benjamin, al usar la matriz anterior, ¿podemos teñir todo excepto el verde? ¿Y también podemos combinar más de un color para conservarlo? ejemplo conservar blanco y verde, tinte resto. Por favor, su respuesta me ayudará
user954299

2

Hice un pequeño probador ColorMatrixFilter, basado en el siguiente fragmento:

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

Puede consultarlo aquí: https://play.google.com/store/apps/details?id=org.vaelostudio.colormatrixtester


1

Aunque se pueden lograr muchos efectos útiles usando el ColorMatrix, personalmente consideraría usar un ColorMap[]junto conImageAttributes . Al hacer esto, podemos definir qué colores deben reemplazarse con qué colores.


¿Qué es ColorMap? Creo que no hay tal clase disponible en las librerías estándar de java / android.
ılǝ

1

La forma en que lo resolví es comenzar con una imagen en escala de grises.

original ---> grayImage

puede hacerlo fácilmente en Photoshop, o si necesita hacerlo en código, puede usar el siguiente método.

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
}

Una vez que tenga su imagen en escala de grises, puede usar el modo SUPERPOSICIÓN de PorterDuff para agregar el color deseado.

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

El resultado anterior es el siguiente randomColorGif

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.