Question

I want to apply several color filters in chain to a drawable. Is that possible? Or maybe to create a filter that is the combination of the filters I want to apply.

For example, I would like:

Drawable d = ...;
d.setColorFilter(0x3F000000, Mode.OVERLAY).setColorFilter(0xFF2D2D2D, Mode.SCREEN)
Was it helpful?

Solution 2

This is the approach I ended using: Manipulate the Drawable bitmap on a Canvas and apply as many layers as I need, using Paint, it works not only for color filters, but also for any kind of image blending.

...
Drawable myBackground = createBackground(getResources().getColor(R.color.Green)); 
setBackgroundDrawable(myBackground);
...

private Drawable createBackground(int color) {

    Canvas canvas = new Canvas();
    Bitmap buttonImage = BitmapFactory.decodeResource(getResources(), R.drawable.btn_image);
    Bitmap buttonShadows = BitmapFactory.decodeResource(getResources(), R.drawable.btn_shadows);
    Bitmap buttonHighLights = BitmapFactory.decodeResource(getResources(), R.drawable.btn_highlights);
    Bitmap result = Bitmap.createBitmap(buttonImage.getWidth(), buttonImage.getHeight(), Bitmap.Config.ARGB_8888);

    canvas.setBitmap(result);
    Paint paint = new Paint();
    paint.setFilterBitmap(false);

    // Color
    paint.setColorFilter(new PorterDuffColorFilter(color, Mode.MULTIPLY));
    canvas.drawBitmap(buttonImage, 0, 0, paint);
    paint.setColorFilter(null);
    // Shadows
    paint.setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));
    canvas.drawBitmap(buttonShadows, 0, 0, paint);
    // HighLights
    paint.setXfermode(new PorterDuffXfermode(Mode.SCREEN));
    canvas.drawBitmap(buttonHighLights, 0, 0, paint);

    paint.setXfermode(null);
    return new BitmapDrawable(getResources(), result);
}

Caveat: setBackgroundDrawable(Drawable d) is deprecated, while setBackground(Drawable d) is only available from api 16 on, so if you have like me a min target api-14 max target api-17 you have no "clean" way to set the drawable as background. I sticked with the deprecated call.

OTHER TIPS

After searching a lot I didn't find an answer that quite satisfy my needs. However i've stumbled upon this video called "Practical Image Processing in Android" from 2018 which pointed me to the right direction : the ImageFilterView class. You can find the documentation here.

Seeing the way they apply multiple filters on the same image is very instructive and happen not to be so hard. It consist of using a ColorMatrix and concatening multiple other ColorMatrix by calling the ColorMatrix.postConcat method. By the end, the result contains all the filters you've applied to it.

Here's a sample code to change the brightness and saturation of an image.

package com.rewieer.imagefilters;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.SeekBar;

public class MainActivity extends AppCompatActivity {
  private ImageView image;
  private SeekBar brightnessSeekBar;
  private SeekBar saturationSeekBar;
  private BitmapDrawable defaultDrawable;

  private int brightness = 0;
  private int saturation = 0;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    image = findViewById(R.id.mainImage);
    defaultDrawable = (BitmapDrawable) image.getDrawable();

    brightnessSeekBar = findViewById(R.id.brightnessSeekBar);
    saturationSeekBar = findViewById(R.id.saturationSeekBar);

    brightnessSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
      @Override
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        brightness = progress;
        redraw();
      }

      @Override
      public void onStartTrackingTouch(SeekBar seekBar) {

      }

      @Override
      public void onStopTrackingTouch(SeekBar seekBar) {

      }
    });

    saturationSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
      @Override
      public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        saturation = progress;
        redraw();
      }

      @Override
      public void onStartTrackingTouch(SeekBar seekBar) {

      }

      @Override
      public void onStopTrackingTouch(SeekBar seekBar) {

      }
    });
  }

  public void redraw() {
    ColorMatrix matrix = new ColorMatrix();
    matrix.postConcat(new ColorMatrix(new float[]{
        1f, 0, 0, 0, brightness,
        0, 1f, 0, 0, brightness,
        0, 0, 1f, 0, brightness,
        0, 0, 0, 1f, 0
    }));

    float MS = 1.0F - saturation;
    float Rt = 0.2999F * MS;
    float Gt = 0.587F * MS;
    float Bt = 0.114F * MS;

    matrix.postConcat(new ColorMatrix(new float[]{
        Rt + saturation, Gt, Bt, 0, 0,
        Rt, Gt + saturation, Bt, 0, 0,
        Rt, Gt, Bt + saturation, 0, 0,
        0, 0, 0, 1f, 0
    }));

    image.setColorFilter(new ColorMatrixColorFilter(matrix));
  }
}

The most important method here being redraw. By looking at the ImageFilterView source code we can also apply warmth and constrat to the image, very easily, by simple copy-pasting their algorithm.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top