11
votes

I have a custom view and in the onDraw(), I am trying to perform bitmap masking. I have a squareBitmap (red color) which fills the whole view, and I have a circleBitmap (blue color) that acts as the mask. I am using the mode: PorterDuff.Mode.DST_IN.

The result I am expecting is a RED filled circle. I get that, but I also get a BLACK opaque background. I don't want this opaque background, rather it should be transparent. Figure 1 is the result I got, and Figure 2 is the result that I am looking for.

Figure 1 : The Result I got Figure 2: The Result that I am looking for

My code: (I have moved everything inside onDraw() for the purpose of this question)

protected void onDraw(Canvas canvas) {

    final int width = canvas.getWidth();
    final int height = canvas.getHeight();

    Bitmap circleBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas circleCanvas = new Canvas(circleBitmap);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
    p.setStyle(Paint.Style.FILL_AND_STROKE);
    p.setColor(Color.BLUE);
    circleCanvas.drawCircle(width / 2, height / 2, width / 2, p);

    p.setColor(Color.RED);
    Bitmap squareBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas squareCanvas = new Canvas(squareBitmap);
    final Rect squareRect = new Rect(0, 0, width, height);
    squareCanvas.drawRect(squareRect, p);

    Paint q = new Paint(Paint.ANTI_ALIAS_FLAG);
    canvas.drawBitmap(squareBitmap, 0, 0, q);
    q.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawBitmap(circleBitmap, 0, 0, q);
    q.setXfermode(null);
}


Where am I going wrong? How can I avoid this black opaque background?

1
Try DST_OUT instead of DST_IN. Or SRC_IN. - Phantômaxx
@FrankN.Stein No, it's not working. Now I get the black filled circle and a red background. The background is red because of the squareBitmap, but why is the circle black? Ideally, the circle should be transparent and hence should be blue (the color of the background layout). - Henry
You should find an answer here: ssp.impulsetrain.com/porterduff.html - Phantômaxx
@FrankN.Stein Thank you, but this link gives an idea on how the blending modes work, but didn't explain about the fix for my problem. I finally fixed by problem by setting a hardware layer to my custom view. - Henry

1 Answers

16
votes

It seems that to get the Xfermode done, I need to redirect the drawing to an offscreen bitmap. So adding the following fixed my problem.

canvas.saveLayer(0, 0, canvas.getWidth(), canvas.getHeight(), q);

But then again, the documentation of saveLayer says to avoid using this method since it is expensive. It is suggested to use a harware layer instead of this method.

Avoid using this method, especially if the bounds provided are large, or if the CLIP_TO_LAYER_SAVE_FLAG is omitted from the saveFlags parameter. It is recommended to use a hardware layer on a View to apply an xfermode, color filter, or alpha, as it will perform much better than this method.

Hence, intead of saveLayer method, I was able to fix it by adding the following:

setLayerType(LAYER_TYPE_HARDWARE, q);