10

I am creating bitmap, next i am drawing second solid color bitmap on top of it. And now i want to change first bitmap, so solid color that i drawed on it will be transparent.

Or simply, i want to remove all pixels of one color from bitmap. I havie tried every colorfilter, and xfermode with no luck, is there any other possibility to remove color other that doing it pixel by pixel?

ZZZ
  • 678
  • 2
  • 10
  • 26

3 Answers3

13

This works for removing a certain color from a bitmap. The main part is the use of AvoidXfermode. It should also work if trying to change one color to another color.

I should add that this answers the question title of removing a color from a bitmap. The specific question is probably better solved using PorterDuff Xfermode like the OP said.

// start with a Bitmap bmp

// make a mutable copy and a canvas from this mutable bitmap
Bitmap mb = bmp.copy(Bitmap.Config.ARGB_8888, true);
Canvas c = new Canvas(mb);

// get the int for the colour which needs to be removed
Paint p = new Paint();
p.setARGB(255, 255, 0, 0); // ARGB for the color, in this case red
int removeColor = p.getColor(); // store this color's int for later use

// Next, set the alpha of the paint to transparent so the color can be removed.
// This could also be non-transparent and be used to turn one color into another color            
p.setAlpha(0);

// then, set the Xfermode of the pain to AvoidXfermode
// removeColor is the color that will be replaced with the pain't color
// 0 is the tolerance (in this case, only the color to be removed is targetted)
// Mode.TARGET means pixels with color the same as removeColor are drawn on
p.setXfermode(new AvoidXfermode(removeColor, 0, AvoidXfermode.Mode.TARGET));

// draw transparent on the "brown" pixels
c.drawPaint(p);

// mb should now have transparent pixels where they were red before
user487252
  • 131
  • 3
  • 1
    This doesn't seem to work in API level 16, and AvoidXferMode has been deprecated without explanation. This solution does indeed work all the way up through API level 15, though. – Leo Accend Aug 20 '12 at 08:12
  • 1
    @LeoAccend Looking at the change history for the source code, it appears to have been deprecated because it is not supported with hardware acceleration. – Dan Dyer Jan 22 '13 at 01:29
4

user487252's solution works like a charm up until API level 16 (Jelly Bean), after which AvoidXfermode does not seem to work at all.

In my particular use case, I have rendered a page of a PDF (via APV PDFView) into a pixel array int[] that I am going to pass into Bitmap.createBitmap( int[], int, int, Bitmap.Config ). This page contains line art drawn onto a white background, and I need to remove the background while preserving the anti-aliasing.

I couldn't find a Porter-Duff mode that did exactly what I wanted, so I ended up buckling and iterating through the pixels and transforming them one by one. The result was surprisingly simple and performant:

int [] pixels = ...;

for( int i = 0; i < pixels.length; i++ ) {
    // Invert the red channel as an alpha bitmask for the desired color.
    pixels[i] = ~( pixels[i] << 8 & 0xFF000000 ) & Color.BLACK;
}

Bitmap bitmap = Bitmap.createBitmap( pixels, width, height, Bitmap.Config.ARGB_8888 );

This is perfect for drawing line art, since any color can be used for the lines without losing the anti-aliasing. I'm using the red channel here, but you can use green by shifting 16 bits instead of 8, or blue by shifting 24.

Community
  • 1
  • 1
Leo Accend
  • 321
  • 2
  • 10
0

Pixel by pixel is not a bad option. Just don't call setPixel inside your loop. Fill an array of argb ints with getPixels, modify it in place if you don't need to preserve the original, and then call setPixels at the end. You can do this row-by-row if memory is a concern, or you can just do the whole thing in one shot. You don't need to fill a whole bitmap for your overlay color since you'd just be doing a simple replace (if current pixel is color1, set to color2).

Rich
  • 36,270
  • 31
  • 115
  • 154
  • Thanks, but i finally figured out how to do it with porterduff, xfermode(xor) did it, but applied to other bitmap, first i merged mask and source and tried to draw it on canvas with colorfilter, but finally i figured out that i should use xfermode while drawing mask on source, not canvas :) – ZZZ Jun 23 '11 at 12:47