Hiểu cách sử dụng ColorMatrix và ColorMatrixColorFilter để sửa đổi màu sắc có thể vẽ được


114

Tôi đang làm việc trên giao diện người dùng cho một ứng dụng và tôi đang cố gắng sử dụng các biểu tượng thang độ xám và cho phép người dùng thay đổi chủ đề thành màu họ chọn. Để làm điều này, tôi đang cố gắng chỉ áp dụng ColorFilter của một số loại để phủ một màu lên trên phần có thể vẽ. Tôi đã thử sử dụng PorterDuff.Mode.MULTIPLY và nó hoạt động gần như chính xác như những gì tôi cần, ngoại trừ việc người da trắng cũng bị phủ lên bởi màu sắc. Những gì tôi lý tưởng đang tìm kiếm là một cái gì đó giống như chế độ hòa trộn "Màu" trong Photoshop, nơi đồ họa vẫn giữ được độ trong suốt và độ sáng của nó và chỉ sửa đổi màu sắc của hình ảnh. Ví dụ:

văn bản thay thế trở thànhvăn bản thay thế

Sau khi thực hiện một số nghiên cứu, có vẻ như lớp ColorMatrixColorFilter có thể làm những gì tôi cần, nhưng tôi dường như không thể tìm thấy bất kỳ tài nguyên nào chỉ về cách ma trận được sử dụng. Đó là ma trận 4x5, nhưng điều tôi cần biết là cách tôi thiết kế ma trận. Có ý kiến ​​gì không?

CHỈNH SỬA: Vậy được rồi, những gì tôi đã tìm thấy cho đến nay về điều này như sau:

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

Trong đó ma trận này là ma trận nhận dạng (khi được áp dụng, không thay đổi) và các số nằm trong khoảng từ 0 đến 1 (nổi). Ma trận này sẽ được nhân lên với mỗi pixel để chuyển sang màu mới. Vì vậy, đây là nơi mà nó bắt đầu trở nên mờ nhạt đối với tôi. Vì vậy, tôi sẽ nghĩ rằng mỗi pixel sẽ là một vector 1 x 4 chứa các giá trị argb (ví dụ 0.2, 0.5, 0.8, 1) sẽ được chấm với ma trận chuyển đổi. Vì vậy, để tăng gấp đôi cường độ màu đỏ của một hình ảnh, bạn sẽ sử dụng một ma trận như:

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

sẽ cung cấp cho bạn một vectơ (màu) 0.4, 0.5, 0.8, 1 . Từ thử nghiệm hạn chế, điều này có vẻ đúng và hoạt động đúng, nhưng tôi thực sự vẫn gặp phải vấn đề tương tự (tức là người da trắng tăng màu). Đọc thêm cho tôi biết rằng điều này là do nó đang thực hiện chuyển đổi trên các giá trị RGB, trong khi đối với chuyển đổi màu sắc, các giá trị trước tiên phải được chuyển đổi thành giá trị HSL. Vì vậy, có thể tôi có thể viết một lớp đọc hình ảnh và chuyển đổi màu sắc, và vẽ lại hình ảnh với các màu mới. Điều này tạo ra vấn đề KHÁC với StateListDrawables, vì tôi không chắc mình sẽ làm thế nào để lấy từng thứ này trong mã và sửa đổi tất cả chúng, và quá trình diễn ra chậm như thế nào. : /

Hmm, được rồi, vì vậy tôi cho rằng một câu hỏi khác mà tôi sẽ đặt ra là liệu một ma trận có thể được sử dụng để chuyển đổi RGB sang không gian màu khác với thông tin về độ sáng, chẳng hạn như L a b hoặc HSL không? Nếu vậy, tôi chỉ có thể nhân ma trận cho cuộc trò chuyện đó, sau đó thực hiện điều chỉnh màu sắc cho ma trận ĐÓ, sau đó áp dụng ma trận đó làm Bộ lọc màu.


5
Bài viết hay nhất mà tôi tìm thấy về chủ đề này ở đây: active.tutsplus.com/tutorials/effects/…
Richard Lalancette 14/10/11

Câu trả lời:


77

Đây là những gì tôi sử dụng cho trò chơi của mình. Đây là phần tổng hợp các phần khác nhau được tìm thấy trên các bài báo khác nhau trên các trang web. Tín dụng được chuyển đến tác giả gốc từ các liên kết @see. Lưu ý rằng có thể làm được nhiều việc hơn với ma trận màu. Bao gồm đảo ngược, v.v.

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

Để hoàn thành điều này, tôi nên thêm một ví dụ:

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

4
nó đảm bảo rằng nó không đi ra ngoài ràng buộc và gây ra hiện vật.
Richard Lalancette

1
@RichardLalancette Bạn có thể giải thích tại sao ma trận mattrong mã của bạn có 5 hàng không? Không phải chỉ có 4 hàng (một cho mỗi RGBA)?
ilomambo

1
@RichardLalancette Sau khi xem mã nguồn của ColorMatrix, tôi lấy lại. Hàng cuối cùng trong matkhông bao giờ được sử dụng. Hàm ColorMatrixtạo chỉ lấy 4 hàng đầu tiên (sao chép 20 phao đầu tiên của ma trận nguồn đã cho).
ilomambo

2
Tôi vừa thử nghiệm nó mà không có hàng thứ 5 và kết quả dường như là như nhau.
miguel

2
float lumR = 0.213f là gì; float lumG = 0,715f; float lumB = 0,072f; và tại sao bạn lại chọn những giá trị đó?
Sujay UN

43

đây là mã hoàn chỉnh nếu bạn muốn điều chỉnh độ sáng, độ tương phản, độ bão hòa và màu sắc. Thưởng thức! Cảm ơn rất nhiều đến @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);
}
}

Nó có sẵn để thay đổi độ bão hòa cho chỉ một màu (xanh lam) không?
Filipst

"value << 0" giống với "value"
Amir Uval

Cảm ơn vì một mã có giá trị. Hai nhận xét về adjustContrastphương thức của bạn : (1) biểu thức value<<0có vẻ thừa, vì nó sẽ luôn bằng (chỉ) value. (2)valuelà một số nguyên, không value % 1phải lúc nào cũng bằng 0?
Bliss

Phạm vi giá trị khả dụng cho độ sáng bị sai. Nó phải nằm trong khoảng -255 đến 255 hoặc được chuyển đổi sau đó.
woodii

12

Đối với bất kỳ ai quan tâm đến cách sử dụng ColorMatrixColorFilter. Mẫu tôi đã sử dụng ở đây, đã chuyển đổi mọi pixel thành màu đỏ khi tôi vẽ bitmap trên canvas.

Nhận xét trong lớp là từ: http://developer.android.com/reference/android/graphics/ColorMatrix.html, điều này cung cấp cho bạn một số thông tin chi tiết về cách hoạt động của nó

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

Mã của bạn hoạt động hoàn hảo cho trường hợp của tôi, trong đó tôi cần tô màu một số tệp PNG: gist.github.com/puelocesar/9710910 - Cảm ơn bạn!
Paulo Cesar

9

Lớp học dưới đây là sự cải thiện về các câu trả lời đã được đăng. Điều này giúp bạn dễ dàng đọc và tạo một ColorFiltertừ a Bitmap.

Ví dụ sử dụng:

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

Không có mối quan hệ tuyến tính giữa Hue và RGB. Hue được xác định từng phần theo từng khối 60 ° ( http://en.wikipedia.org/wiki/HSL_color_space#General_approach ) và do đó không có sự chuyển đổi ma trận đơn giản giữa HSV và RGB. Để thay đổi màu sắc của hình ảnh, bạn có thể sử dụng phương pháp sau:

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

Tôi nghĩ rằng phương pháp này sẽ cung cấp cho bạn những gì bạn muốn:

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

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

1
Lưu ý, phương pháp này sẽ không thay đổi màu sắc giống như các phương pháp ColorFilterGenerator được hiển thị ở trên. Để xem những gì nó có thể tạo ra, hãy mở bitmap của bạn trong Photoshop hoặc GIMP. Tạo một lớp trên cùng của hình ảnh và tô nó với màu bạn muốn. Sau đó, đặt chế độ lớp của lớp phủ thành nhân. Chuyển chế độ lớp sang màu để xem trước các phương thức ColorFilterGenerator sẽ làm gì.
Kevin Mark,

6

Giống như phần còn lại của các câu trả lời, tôi đã sử dụng ma trận màu để thực hiện hành vi này, nhưng bạn có thể chuyển vào tài nguyên màu Android thông thường. Ma trận, ánh xạ màu thành một dải giữa giá trị hình ảnh và màu trắng.

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

Xin chào Benjamin, bằng cách sử dụng ma trận trên, chúng ta có thể tô màu mọi thứ ngoại trừ Màu xanh lá cây không ?. Và chúng ta cũng có thể kết hợp nhiều hơn một màu được giữ nguyên? ví dụ bảo quản màu trắng và xanh lá cây, phần còn lại màu. Hãy trả lời của bạn sẽ giúp tôi
user954299

2

Tôi đã tạo một trình thử nghiệm ColorMatrixFilter nhỏ, dựa trên đoạn mã sau:

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

Bạn có thể xem tại đây: https://play.google.com/store/apps/details?id=org.vaelostudio.colormatrixtester


1

Mặc dù rất nhiều tác dụng hữu ích có thể đạt được bằng cách sử dụng ColorMatrixcá nhân tôi sẽ cân nhắc sử dụng ColorMap[]cùng với ImageAttributes. Bằng cách này, chúng ta có thể xác định màu nào nên được thay thế bằng màu nào.


Là gì ColorMap? Tôi nghĩ rằng không có lớp nào như vậy trong java / android libs tiêu chuẩn.
ılǝ

1

Cách tôi giải quyết nó là bắt đầu với một hình ảnh tỷ lệ xám.

ban đầu ---> màu xám

bạn có thể làm điều đó một cách dễ dàng trong photoshop, hoặc nếu bạn cần làm điều đó trong mã, bạn có thể sử dụng phương pháp sau.

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
}

Sau khi có hình ảnh GreyScale, bạn có thể sử dụng chế độ PorterDuff OVERLAY để thêm Màu mong muốn.

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

Kết quả trên như sau randomColorGif

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.