N.M
N.M

Reputation: 11

Android change opacity only on a region of bitmap

I would like to create a brush effect, which when the user drags his/her finger on the screen, the brush only changes the alpha of the image/bitmap below. For example, if the brush opacity is set to 50%, then applying brush should

  1. Set the alpha/opacity of that region, only that region not the whole bitmap.
  2. Leave the underlying colors of the bitmap region unchanged.

That is, the brush should only affect the alpha transparency not the color. I am starting with the following code:

public class DrawingView extends View {

    //drawing path
    private Path drawPath;
    //drawing and canvas paint
    private Paint drawPaint, canvasPaint;
    //initial color
    private int paintColor = 0xFFFF0000, paintAlpha = 255;
    //canvas
    private Canvas drawCanvas;
    //canvas bitmap
    private Bitmap canvasBitmap;

    //constructor
    public DrawingView(Context context, AttributeSet attrs){
        super(context, attrs);
        setupDrawing();
    }

    //prepare drawing
    private void setupDrawing(){
        drawPath = new Path();
        drawPaint = new Paint();
        drawPaint.setColor(paintColor);
        drawPaint.setAntiAlias(true);
        drawPaint.setStrokeWidth(50);
        drawPaint.setStyle(Paint.Style.STROKE);
        drawPaint.setStrokeJoin(Paint.Join.ROUND);
        drawPaint.setStrokeCap(Paint.Cap.ROUND);
        canvasPaint = new Paint(Paint.DITHER_FLAG);
    }

    //view assigned size
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
        drawCanvas = new Canvas(canvasBitmap);
    }

    //draw view
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
        canvas.drawPath(drawPath, drawPaint);
    }

    //respond to touch interaction
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float touchX = event.getX();
        float touchY = event.getY();
        //respond to down, move and up events
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            drawPath.moveTo(touchX, touchY);
            break;
        case MotionEvent.ACTION_MOVE:
            drawPath.lineTo(touchX, touchY);
            break;
        case MotionEvent.ACTION_UP:
            drawPath.lineTo(touchX, touchY);
            drawCanvas.drawPath(drawPath, drawPaint);
            drawPath.reset();
            break;
        default:
            return false;
        }
        //redraw
        invalidate();
        return true;
    }

    //return current alpha
    public int getPaintAlpha(){
        return Math.round((float)paintAlpha/255*100);
    }

    //set alpha
    public void setPaintAlpha(int newAlpha){
        paintAlpha=Math.round((float)newAlpha/100*255);
        drawPaint.setColor(paintColor);
        drawPaint.setAlpha(paintAlpha);
    }
}

As you have noticed, this brush needs to have a fill color. That is what want to avoid. I need a brush with transparency, but not color.

If there is another method to achieve the object, that is fine.

Upvotes: 0

Views: 1323

Answers (1)

kris larson
kris larson

Reputation: 30985

Setting an absolute alpha value is likely going to involve some pixel-twiddling in the bitmap.

The closest Porter-Duff mode for what you want is DST_OUT, which will act like an eraser.

Try this code and see if it comes close:

       drawPaint.setColor(Color.BLACK);  // the color doesn't really matter
       drawPaint.setAlpha(255 - alpha);  // inverse of the alpha result you want
       drawPaint.setXferMode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));

What this is going to do is make the image pixels lighter and lighter every time you draw over the same spot, which probably isn't what you want. In which case you would have to get more complicated and draw the color+alpha paths onto a separate bitmap, then draw that bitmap over your image bitmap using PorterDuff.Mode.DST_OUT.

It's unfortunate that you can't simply implement a custom ColorFilter that will do exactly what you want. All those graphics operations happen in native space and Google doesn't want us mere mortals messing with that stuff.

Upvotes: 0

Related Questions