Search code examples
androidbitmapmaskdrawable

Masking a Drawable/Bitmap on Android


I'm currently looking for a way to use a black and white bitmap to mask the alpha channel of another bitmap or Drawable on Android. I'm curious as to what the best way to do this is. I certainly have a couple of ideas for how to do this, but they are not optimal.

I need to be able to apply a new mask to the image every so often (the black and white bitmap will change every few seconds).

Any feedback on how to achieve this would be greatly appreciated.


Solution

  • 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 accelerated
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
    
    // Using destination shape as a mask
    // For a good explanation of PorterDuff transfer modes : http://ssp.impulsetrain.com/porterduff.html
    maskingPaint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    maskingPaint.setAntiAlias(true);
    
    // Position the mask
    mask.setBounds(<insert your mask bounds here>);
    

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

    @Override
    protected 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!