Masking a Drawable/Bitmap on Android Masking a Drawable/Bitmap on Android android android

Masking a Drawable/Bitmap on Android


My solution is close to @over_optimistic's solution, less one saveLayer() call. I use a Drawable mask instead of a path, in my case it was a disc.

I declared these variables as fields (it's good practice to allocate memory outside of onDraw method):

private Paint maskingPaint = new Paint();private Drawable mask = <insert your drawable here>

Then, somewhere outside of onDraw(), setup the objects:

// Xfermode won't work if hardware acceleratedsetLayerType(View.LAYER_TYPE_SOFTWARE, null);// Using destination shape as a mask// For a good explanation of PorterDuff transfer modes : http://ssp.impulsetrain.com/porterduff.htmlmaskingPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));maskingPaint.setAntiAlias(true);// Position the maskmask.setBounds(<insert your mask bounds here>);

Then finally, the onDraw() method applies the mask:

@Overrideprotected synchronized void onDraw(Canvas canvas){    // Draw the mask first, making it the PorterDuff destination    mask.draw(canvas);    // Save the layer with the masking paint, that will be applied on restore()    // Using CLIP_TO_LAYER_SAVE_FLAG to avoid any overflow of the masked image outside the mask bounds.    Rect bounds = mask.getBounds();    canvas.saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, maskingPaint,             Canvas.CLIP_TO_LAYER_SAVE_FLAG);    // Draw the shape offscreen, making it the PorterDuff source    super.onDraw(canvas);    // Apply the source to the destination, using SRC_IN transfer mode    canvas.restore();}

For a better understanding of the transfer modes, I referred to http://ssp.impulsetrain.com/porterduff.html.That page is pretty interesting to read. After that, with the same kind of code you'll be able to acheive much more than a simple mask!


I got it working, so it's something like this

    // we first same the layer, rectF is the area of interest we plan on drawing    // this will create an offscreen bitmap    canvas.saveLayer(rectF, null, Canvas.MATRIX_SAVE_FLAG            | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG            | Canvas.FULL_COLOR_LAYER_SAVE_FLAG            | Canvas.CLIP_TO_LAYER_SAVE_FLAG);    // draw our unmasked stuff    super.draw(canvas);    // We same a layer again but this time we pass a paint object to mask    // the above layer    maskPaint = new Paint()    maskPaint.setColor(0xFFFFFFFF);    maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));    canvas.saveLayer(rectF, maskPaint,            Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG                    | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG                    | Canvas.FULL_COLOR_LAYER_SAVE_FLAG                    | Canvas.CLIP_TO_LAYER_SAVE_FLAG);    // we draw the mask which is black and white. In my case    // I have a path, and I use a blurPaint which blurs the mask slightly    // You can do anti aliasing or whatever you which. Just black & white    canvas.drawPath(path, blurPaint);    // We restore twice, this merges the results upward    // as each saveLayer() allocates a new drawing bitmap    canvas.restore();    canvas.restore();


I made a maskable layout.It's a framelayout where you can specifiy the xporterduffmode and the mask.You can find it here: https://github.com/christophesmet/android_maskable_layout