1

Pulling my hear out over this one. I have a background bitmap that I want to overlay another bitmap on top of that has transparent cutouts. I have no problem doing that if the cutout is a basic shape, but I need the cutout to be the intersection of two circles (sort of a leaf shape). I tried making a third bitmap to produce the a cutout template but I can't even get a clean representation of the cutout let alone get it to work as a cutout template.

Anyone know how to do something like this? Here is my (simplified) attempt:

@Override
public void draw(Canvas canvas) {
    float w = canvas.getWidth();
    float h = canvas.getHeight();

    // just used to set some proportions
    float off = 300f;

    Paint paint = new Paint();

    // make a background bitmap with a yellow to green gradient
    Bitmap bitmapBkg = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
    Canvas canvasBkg = new Canvas(bitmapBkg);
    paint.reset();
    paint.setShader(new LinearGradient(0, h/2 - off, 0, h/2 + off, Color.YELLOW, Color.GREEN, Shader.TileMode.CLAMP));
    canvasBkg.drawRect(new RectF(0, 0, w, h), paint);

    // make an overlay bitmap with a red to magenta gradient which will have the cutouts
    Bitmap bitmapOver = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
    Canvas canvasOver = new Canvas(bitmapOver);
    paint.reset();
    paint.setShader(new LinearGradient(0, h/2 - off, 0, h/2 + off, Color.RED, Color.MAGENTA, Shader.TileMode.CLAMP));
    canvasOver.drawRect(new RectF(0, 0, w, h), paint);

    // make a bitmap of intersecting circles to be used as the cutout shape
    Bitmap bitmapCut = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
    Canvas canvasCut = new Canvas(bitmapCut);

    paint.reset();
    paint.setColor(Color.BLACK);
    //paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    canvasCut.drawCircle(w / 2 - (off / 2 ), h / 2, off, paint);

    paint.reset();
    paint.setColor(Color.BLACK);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.XOR));
    canvasCut.drawCircle(w / 2 + (off / 2), h / 2, off, paint);

    // apply cutout to overlay
    paint.reset();
    paint.setColor(Color.BLACK);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_ATOP));
    canvasOver.drawBitmap(bitmapCut, 0, 0, paint);

    // draw background and overlay onto main canvas
    paint.reset();
    paint.setColor(Color.BLACK);
    canvas.drawBitmap(bitmapBkg, 0, 0, paint);
    canvas.drawBitmap(bitmapOver, 0, 0, paint);
}

Here is an image of what I am getting:

enter image description here

What I am trying to get would have the outside portion also red-magenta; only the eye-shape in the middle should be yellow-green.

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
trans
  • 1,411
  • 1
  • 13
  • 13
  • Maybe combining the images by taking advantage of PorterDuff? http://stackoverflow.com/questions/8280027/what-does-porterduff-mode-mean-in-android-graphics-what-does-it-do – Phantômaxx Aug 08 '16 at 14:47
  • That's what I've been trying to use. – trans Aug 08 '16 at 17:56
  • And what went wrong? – Phantômaxx Aug 08 '16 at 19:02
  • The above code shows the closest I've been able to get. I get a cutout but the rest of background should be the red-magenta overlay -- I am only getting the circles. – trans Aug 08 '16 at 20:01
  • Could you post a picture of what you desire and what you get instead? – Phantômaxx Aug 08 '16 at 20:22
  • I added a link to a clip of what I am getting. I can't really give one of what I desire b/c I can't get it to work ;-) – trans Aug 08 '16 at 20:33
  • OK, maybe I get what you want: Just the eye in a lime gradient on a magenta gradient background. Well, I think you're on the right path. You just want the intersection of 2 circles. The background is just a linear gradient. Maybe a 2 step process will do (one step being the creation of the "eye" and the other being the cut out of the combined circles from the background). OR maybe you could just draw the eye by using some of the drawXYZ (`drawArc()`, for instance) functions on a linear gradiented background. – Phantômaxx Aug 08 '16 at 20:48

1 Answers1

0

Turns out the trick was adding yet another layer.

    // make a secondary overlay that cuts out the whole circles
    Bitmap bitmapOver2 = Bitmap.createBitmap((int) w, (int) h, Bitmap.Config.ARGB_8888);
    Canvas canvasOver2 = new Canvas(bitmapOver2);
    paint.reset();
    paint.setShader(new LinearGradient(0, h / 2 - off, 0, h / 2 + off, Color.RED, Color.MAGENTA, Shader.TileMode.CLAMP));
    canvasOver2.drawRect(new RectF(0, 0, w, h), paint);
    paint.reset();
    paint.setColor(Color.BLACK);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
    canvasOver2.drawCircle(w / 2 - (off / 2), h / 2, off, paint);
    canvasOver2.drawCircle(w / 2 + (off / 2), h / 2, off, paint);

Applying it like so:

    // draw background and overlay onto main canvas
    paint.reset();
    paint.setColor(Color.BLACK);
    canvas.drawBitmap(bitmapBkg, 0, 0, paint);
    canvas.drawBitmap(bitmapOver2, 0, 0, paint);
    canvas.drawBitmap(bitmapOver, 0, 0, paint);

Basically it is a bit of a trick. It draws the mid-tier backdrop twice, once with a full circle cutouts and the other with the eye-shape cutout. The two fit together just right to pull off the desired effect.

Of course @Rotwang, you are probably right. Using Path and arcTo() would be a much better solution. The only reason I avoided that approach is b/c arcTo() is an api 21+ feature. So far I've manged to keep the api to 17+. But if anyone else would like to provide an arcTo() solution for completeness that would be cool to compare.

trans
  • 1,411
  • 1
  • 13
  • 13