17

I am trying to cut a circle from a square bitmap using following code

        Canvas canvas=new Canvas(bitmapimg );
        int circleXCoord = bitmapimg .getWidth() / 2;
        int circleYCoord = bitmapimg .getHeight() / 2;
        int circleRadius = bitmapimg .getWidth() / 2;

        Rect rect = new Rect(circleXCoord - circleRadius, circleYCoord - circleRadius, circleXCoord + circleRadius, circleYCoord + circleRadius);

        int width = rect.width();
        int height = rect.height();

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(Color.BLUE);
        canvas.drawRect(rect, paint);
        canvas.drawBitmap(bitmapimg , rect, rect, paint);
        Path p = new Path();
        p.addCircle(circleXCoord, circleYCoord, width / 2F, Path.Direction.CW);
        canvas.clipPath(p, Region.Op.DIFFERENCE);
        canvas.drawColor(0, PorterDuff.Mode.CLEAR);

The idea is to attach a square (rectangular) bitmap to canvas and then clip a circular path. Clear out the difference between the rectangle and circle (make it transparent).

The code works fine for Android 4, but on Android 2.3.3 device, the difference area is appearing black rather that transparent.

Am I missing something here or PorterDuff.Mode.CLEAR is not supported in gingerbread? Is there a better way to cut a circle from a square in Android?

Kamal
  • 5,462
  • 8
  • 45
  • 58

3 Answers3

40

Seems like PorterDuff.Mode.Clear did not work for gingerbread

Solved the problem (cut circle from square using this code)

 public Bitmap BitmapCircularCroper(Bitmap bitmapimg){
    Bitmap output = Bitmap.createBitmap(bitmapimg.getWidth(),
            bitmapimg.getHeight(), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(output);

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, bitmapimg.getWidth(),
            bitmapimg.getHeight());

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawCircle(bitmapimg.getWidth() / 2,
            bitmapimg.getHeight() / 2, bitmapimg.getWidth() / 2, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmapimg, rect, rect, paint);
    return output;

}
SAKhan
  • 249
  • 4
  • 16
Kamal
  • 5,462
  • 8
  • 45
  • 58
  • getting circular bitmap but why square transparent area shown black in my case? – Ram Jan 11 '17 at 08:50
5
import android.graphics.PorterDuff.Mode;
import android.graphics.Bitmap.Config;

public static Bitmap getCircularBitmap(Bitmap bitmap) 
{
    Bitmap output;

    if (bitmap.getWidth() > bitmap.getHeight()) {
        output = Bitmap.createBitmap(bitmap.getHeight(), bitmap.getHeight(), Config.ARGB_8888);
    } else {
        output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getWidth(), Config.ARGB_8888);
    }

    Canvas canvas = new Canvas(output);

    final int color = 0xff424242;
    final Paint paint = new Paint();
    final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());

    float r = 0;

    if (bitmap.getWidth() > bitmap.getHeight()) {
        r = bitmap.getHeight() / 2;
    } else {
        r = bitmap.getWidth() / 2;
    }

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawCircle(r, r, r, paint);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);

    return output;
}
indrajeet
  • 341
  • 5
  • 9
3

For anyone that's still looking at this, this answer will cause a few issues.

1.) The canvas that you are creating in this instance will not have hardware acceleration. Even though your Paint object has anti-aliasing, the canvas will not. This will cause artifacting when you decide to paint this back to your original canvas in your onDraw() call.

2.) This takes a lot more resources. You have to create a second Bitmap (which can cause OOM), and a secondary Canvas as well as all of the different alterations you have to do.

Please check out Romain Guy's answer. You create a BitmapShader and then create a RoundRect that gives you a Circle. You just need to know the dimensions of your RectF so that it can determine the circle properly.

This means that if you know the center point (x, y) and radius, you can easily determine the RectF.

left = x - radius;
top = y - radius;
right = x + radius;
bottom = y + radius;

This also means that with this solution posted below you only have to draw to the screen once, everything else is done in the off-screen buffer.

http://www.curious-creature.com/2012/12/11/android-recipe-1-image-with-rounded-corners/

The best solution is found here:

BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);

RectF rect = new RectF(0.0f, 0.0f, width, height);

// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);
user1090347
  • 119
  • 5