Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PorterduffXfermode: Clear a section of a bitmap

The goal is to draw a bitmap and over the top of something, and draw shapes that ERASE the underlying area of the bitmap.

I have a proof of concept to try and understand how I should go about this. I have found numerous hints about using:

android.graphics.PorterDuff.Mode.CLEAR 

The code below creates a screen with a blue background and adds a custom view. This view draws on its canvas from bottom to top: a pink background, a bitmap with a slight inset to show the pink background, and a yellow circle for each PorterDuffXfermode.

import android.app.Activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.PorterDuffXfermode; import android.graphics.Paint.Style; import android.graphics.PorterDuff.Mode; import android.graphics.RectF; import android.os.Bundle; import android.view.View; import android.view.Window; import android.widget.RelativeLayout;  public class Test extends Activity {     Drawing d = null;          @Override     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         requestWindowFeature(Window.FEATURE_NO_TITLE);                  RelativeLayout.LayoutParams rlp = null;                  // Create the view for the xfermode test         d = new Drawing(this);         rlp = new RelativeLayout.LayoutParams(600, 900);         rlp.addRule(RelativeLayout.CENTER_IN_PARENT);         d.setLayoutParams(rlp);                  RelativeLayout rl = new RelativeLayout(this);         rl.setBackgroundColor(Color.rgb(0, 0, 255));         rl.addView(d);                  // Show the layout with the test view         setContentView(rl);     }          public class Drawing extends View {         Paint[] pDraw = null;         Bitmap bm = null;                  public Drawing(Context ct) {             super(ct);                          // Generate bitmap used for background             bm = BitmapFactory.decodeFile("mnt/sdcard/Pictures/test.jpg");                          // Generate array of paints             pDraw = new Paint[16];                          for (int i = 0; i<pDraw.length; i++) {                 pDraw[i] = new Paint();                 pDraw[i].setARGB(255, 255, 255, 0);                 pDraw[i].setStrokeWidth(20);                 pDraw[i].setStyle(Style.FILL);             }                          // Set all transfer modes             pDraw[0].setXfermode(new PorterDuffXfermode(Mode.CLEAR));             pDraw[1].setXfermode(new PorterDuffXfermode(Mode.DARKEN));             pDraw[2].setXfermode(new PorterDuffXfermode(Mode.DST));             pDraw[3].setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));             pDraw[4].setXfermode(new PorterDuffXfermode(Mode.DST_IN));             pDraw[5].setXfermode(new PorterDuffXfermode(Mode.DST_OUT));             pDraw[6].setXfermode(new PorterDuffXfermode(Mode.DST_OVER));             pDraw[7].setXfermode(new PorterDuffXfermode(Mode.LIGHTEN));             pDraw[8].setXfermode(new PorterDuffXfermode(Mode.MULTIPLY));             pDraw[9].setXfermode(new PorterDuffXfermode(Mode.SCREEN));             pDraw[10].setXfermode(new PorterDuffXfermode(Mode.SRC));             pDraw[11].setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));             pDraw[12].setXfermode(new PorterDuffXfermode(Mode.SRC_IN));             pDraw[13].setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));             pDraw[14].setXfermode(new PorterDuffXfermode(Mode.SRC_OVER));             pDraw[15].setXfermode(new PorterDuffXfermode(Mode.XOR));         }                  @Override         public void onDraw(Canvas canv) {             // Background colour for canvas             canv.drawColor(Color.argb(255, 255, 0, 255));                          // Draw the bitmap leaving small outside border to see background             canv.drawBitmap(bm, null, new RectF(15, 15, getMeasuredWidth() - 15, getMeasuredHeight() - 15), null);              float w, h;             w = getMeasuredWidth();             h = getMeasuredHeight();              // Loop, draws 4 circles per row, in 4 rows on top of bitmap             // Drawn in the order of pDraw (alphabetical)             for(int i = 0; i<4; i++) {                 for(int ii = 0; ii<4; ii++) {                     canv.drawCircle((w / 8) * (ii * 2 + 1), (h / 8) * (i * 2 + 1), w / 8 * 0.8f, pDraw[i*4 + ii]);                 }             }         }     }  } 

This is the result of the test:

enter image description here

The CLEAR mode is the top left, which shows as black.

In another example where I was trying to use this I had a DialogFragment where CLEAR mode erased the entire DialogFragment so that the activity beneath could be seen. Hence the reason I used many different background colours in this test.

Could this possibly be clearing the pixels of the entire activity like that other example led me to believe? I would've thought only the pixels of canvas related to the view could be erased, but in my other example the pixels of the custom view, underlying image view and DialogFragment background were all cleared.

Could someone please help me understand what exactly is going on?

EDIT: I reproduced an example confirming my suspicions. When adding this exact custom view but in a DialogFragment, the underlying activity becomes visible.

enter image description here

This seems a pretty clear indicator to me that the Mode.CLEAR is somehow erasing the canvas of the views underneath as well? My guess would be the black in the first example is that of the top level view?

like image 641
btalb Avatar asked Jul 05 '12 03:07

btalb


1 Answers

The problem is hardware acceleration. Turn it OFF for the particular View that you are painting with CLEAR. If you're using a custom view, do this in the constructors:

if (android.os.Build.VERSION.SDK_INT >= 11)  {      setLayerType(View.LAYER_TYPE_SOFTWARE, null); } 

You can also call setLayerType on a view reference.

like image 143
metaphyze Avatar answered Oct 16 '22 19:10

metaphyze