I try to erase parts of a bitmap in my Android application by using Porter-Duff Xfermodes.
I have a green background which is overlayed by a blue bitmap. When I touch the screen a "hole" in the overlaying bitmap is supposed to be created making the green background visible. Instead of a hole my current code produces a black dot.
Below is my code. Any ideas, what I am doing wrong here?
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(new DrawView(this));
}
public class DrawView extends View implements OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Canvas bitmapCanvas;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.GREEN);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.RGB_565);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.BLUE);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
eraserPaint.setAntiAlias(true);
}
@Override
public void onDraw(Canvas canvas) {
bitmapCanvas.drawColor(Color.BLUE);
bitmapCanvas.drawCircle(x, y, 10, eraserPaint);
canvas.drawBitmap(bitmap, 0, 0, paint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
invalidate();
return true;
}
}
You can use eraseColor on bitmap to set its color to Transparent. It will useable again without recreating it.
Multiplies or screens the source and destination depending on the destination color. PorterDuff.Mode. SCREEN. Adds the source and destination pixels, then subtracts the source pixels multiplied by the destination.
The Canvas class holds the "draw" calls. To draw something, you need 4 basic components: A Bitmap to hold the pixels, a Canvas to host the draw calls (writing into the bitmap), a drawing primitive (e.g. Rect, Path, text, Bitmap), and a paint (to describe the colors and styles for the drawing).
Here is working code... may help somebody
public class ImageDemo extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new Panel(this));
}
class Panel extends View {
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mPaint;
Bitmap bitmap;
Canvas pcanvas;
int x = 0;
int y =0;
int r =0;
public Panel(Context context) {
super(context);
Log.v("Panel", ">>>>>>");
setFocusable(true);
setBackgroundColor(Color.GREEN);
// setting paint
mPaint = new Paint();
mPaint.setAlpha(0);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setAntiAlias(true);
// getting image from resources
Resources r = this.getContext().getResources();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.mickey);
// converting image bitmap into mutable bitmap
bitmap = bm.createBitmap(295, 260, Config.ARGB_8888);
pcanvas = new Canvas();
pcanvas.setBitmap(bitmap); // drawXY will result on that Bitmap
pcanvas.drawBitmap(bm, 0, 0, null);
}
@Override
protected void onDraw(Canvas canvas) {
// draw a circle that is erasing bitmap
pcanvas.drawCircle(x, y, r, mPaint);
canvas.drawBitmap(bitmap, 0, 0,null);
super.onDraw(canvas);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// set parameter to draw circle on touch event
x = (int) event.getX();
y = (int) event.getY();
r =20;
// At last invalidate canvas
invalidate();
return true;
}
}
}
First thought, I'm not sure if setting alpha to 0 on your erase paint object is a good idea. That might make the whole thing ineffective.
Also, you should always use Bitmap.Config.ARGB_8888 if you're dealing with alphas.
If you're having trouble with the PorterDuff stuff, though, I would suggest simplifying your approach to ONLY do that (temporarily). That will help you narrow down the part which isn't working. Comment out everything to do with touch and view updates.
Then you can single out what part of the drawing isn't working right. Set up your constructor like this:
DrawView()
{
/* Create the background green bitmap */
...
/* Create foreground transparent bitmap */
...
/* Draw a blue circle on the foreground bitmap */
...
/* Apply the foreground to the background bitmap
using a PorterDuff method */
...
}
onDraw()
{
/* Simply draw the background bitmap */
...
}
If you set things up like that, you should be able to tell how your PD method is affecting the green bitmap, and change things accordingly.
Here is another advancement for your solution ... See Demo example
public class MainActivity extends Activity {
Bitmap bp;
Canvas bitmapCanvas;
DrawView drawImg;
LinearLayout ln1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ln1 = (LinearLayout) findViewById(R.id.ln1);
drawImg = new DrawView(this);
ln1.addView(drawImg);
}
public class DrawView extends View implements View.OnTouchListener {
private int x = 0;
private int y = 0;
Bitmap bitmap;
Path circlePath;
Paint circlePaint;
private final Paint paint = new Paint();
private final Paint eraserPaint = new Paint();
public DrawView(Context context){
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
this.setOnTouchListener(this);
// Set background
this.setBackgroundColor(Color.CYAN);
bp = BitmapFactory.decodeResource(getResources(), R.drawable.bg);
// Set bitmap
bitmap = Bitmap.createBitmap(320, 480, Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas();
bitmapCanvas.setBitmap(bitmap);
bitmapCanvas.drawColor(Color.TRANSPARENT);
bitmapCanvas.drawBitmap(bp, 0, 0, null);
circlePath = new Path();
circlePaint = new Paint();
circlePaint.setAntiAlias(true);
circlePaint.setColor(Color.BLUE);
circlePaint.setStyle(Paint.Style.STROKE);
circlePaint.setStrokeJoin(Paint.Join.MITER);
circlePaint.setStrokeWidth(4f);
// Set eraser paint properties
eraserPaint.setAlpha(0);
eraserPaint.setStrokeJoin(Paint.Join.ROUND);
eraserPaint.setStrokeCap(Paint.Cap.ROUND);
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
eraserPaint.setAntiAlias(true);
}
@Override
public void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, paint);
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
canvas.drawPath(circlePath, circlePaint);
}
public boolean onTouch(View view, MotionEvent event) {
x = (int) event.getX();
y = (int) event.getY();
bitmapCanvas.drawCircle(x, y, 30, eraserPaint);
circlePath.reset();
circlePath.addCircle(x, y, 30, Path.Direction.CW);
int ac=event.getAction();
switch(ac){
case MotionEvent.ACTION_UP:
Toast.makeText(MainActivity.this, String.valueOf(x), Toast.LENGTH_SHORT).show();
circlePath.reset();
break;
}
invalidate();
return true;
}
}
}
read more
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With