How to crop circular area from bitmap in Android
Asked Answered
D

19

121

I have a bitmap and I want to crop a circular region from this bitmap. All pixels outside the circle should be transparent. How can I do this?

enter image description here

Discrete answered 13/8, 2012 at 10:48 Comment(0)
D
230

After long brainstorming I have found the solution

public Bitmap getCroppedBitmap(Bitmap bitmap) {
    Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
            bitmap.getHeight(), 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());

    paint.setAntiAlias(true);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
    canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
            bitmap.getWidth() / 2, paint);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(bitmap, rect, rect, paint);
    //Bitmap _bmp = Bitmap.createScaledBitmap(output, 60, 60, false);
    //return _bmp;
    return output;
}
Discrete answered 23/8, 2012 at 9:56 Comment(12)
You could also do this by clipping the bitmap against a circular clipping path. You could either do this every time you draw the bitmap, which means you'd never actually create a bitmap with transparent pixels, or you could draw the clipped bitmap into a buffer that has been erased to transparent beforehand. I think either would be a bit faster and simpler than this.Kropp
Thanks. your code works spectacular. Now I can also crop using path (Polygon).Semirigid
This method could be made static and used in a non-instantiated utility class of similar static methods.Ochlocracy
Yes ,its up to you whether you want it be static or not,its also depends upon the requirement.Discrete
I am using this and still seem to be getting problems with jagged edges on my image I'm cutting. Is there something else that I'm missing?Baba
I am getting circular image but showing a black background. I want to remove that. How to that?Metaplasia
Should not you use minimum of height and width divided by 2 as radius here? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);Aloeswood
What is the paint color used for? It works fine without setting a colorProof
There're 3 key point: 1) Create an empty bitmap and draw a circle. 2) Set xfermode to SRC_IN. 3) Draw the real bitmap to that canvas bounds. So, the paint color and other canvas drawing are of no use.Yaker
For future reference: A Rejected Edit titled "half of the maximum of width and height is used as the radius of the image so as to get perfect cirular image" suggested replacing the existing line: bitmap.getWidth() / 2, paint); with the new line: bitmap.getWidth>bitmap.getHeight?bitmap.getWidth() / 2:bitmap.getHeight() / 2, paint);; This edit was intended to address the author of the post and makes no sense as an edit. It should have been written as a comment or an answer.Hepcat
For the radius param in drawCircle, I think it would be better to do it like this: bitmap.getWidth() > bitmap.getHeight() ? (float) bitmap.getHeight() / 2 : (float) bitmap.getWidth() / 2, so that when the width is smaller than the height, we should use the width as the diameter of the circle, basically, whichever one is smaller, we use that as the diameter, and this works for both long and wide picturesEscarpment
image border is getting broken.Prosimian
G
49

to generate Circle from rectangles

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;
}
Gestation answered 21/3, 2013 at 0:43 Comment(1)
i would suggest using using two imageviews in a framelayout with the top imageview with transparent circle cut out.Gestation
K
42

You Can make your imageview circular using RoundedBitmapDrawable

here is the code for achieving roundedImageview:

ImageView profilePic=(ImageView)findViewById(R.id.user_image);

//get bitmap of the image
Bitmap imageBitmap=BitmapFactory.decodeResource(getResources(),  R.drawable.large_icon);
RoundedBitmapDrawable roundedBitmapDrawable=RoundedBitmapDrawableFactory.create(getResources(), imageBitmap);

//setting radius
roundedBitmapDrawable.setCornerRadius(50.0f);
roundedBitmapDrawable.setAntiAlias(true);
profilePic.setImageDrawable(roundedBitmapDrawable);
Kampala answered 22/1, 2015 at 18:43 Comment(3)
Thanks for showing this v4 Support library option. I don't think I would have found out about it otherwise.Matelote
and using setCircular(true) instead of setCornerRadius(50.0f) makes the drawable a circle. note: the image must be square or the aspect ratio is defect...Entrails
Deprecated! It is no longer maintainedApophasis
W
34

@Gene made a comment on the answer above that suggested using clipPath as an option for cropping an image as a circle.

The following is a clean implementation of this:

    public static Bitmap GetBitmapClippedCircle(Bitmap bitmap) {

        final int width = bitmap.getWidth();
        final int height = bitmap.getHeight();
        final Bitmap outputBitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);

        final Path path = new Path();
        path.addCircle(
                  (float)(width / 2)
                , (float)(height / 2)
                , (float) Math.min(width, (height / 2))
                , Path.Direction.CCW);

        final Canvas canvas = new Canvas(outputBitmap);
        canvas.clipPath(path);
        canvas.drawBitmap(bitmap, 0, 0, null);
        return outputBitmap;
    }

This could be added to a utility class.

Wafer answered 19/3, 2013 at 1:10 Comment(4)
I was just about to post very similar code. The problem is according to developer.android.com/guide/topics/graphics/hardware-accel.html, clipPath is not supported with hardware acceleration. I actually ran into that problem in an app and wondered what was going on. Newer hardware seems to fix this, however (like Google tablets). One possible further cleanup to your code: You don't need the rect-to-rect conversion when drawing the bitmap. You can just say c.drawBitmap(b, 0, 0, null);, which uses the default identity transform.Kropp
how did you get around using clipPath while using hardware acceleration?Polacca
I was originally using this solution before but the output had jagged edges. The solution from @Discrete works betterCupola
Works great for cropping images used in notification on status barImpinge
S
14

I think this solution works better with any type of rectangle, change the pixel size if you want image small or large :

public static Bitmap getCircleBitmap(Bitmap bm) {

        int sice = Math.min((bm.getWidth()), (bm.getHeight()));

        Bitmap bitmap = ThumbnailUtils.extractThumbnail(bm, sice, sice);

        Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);

        Canvas canvas = new Canvas(output);

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

        paint.setAntiAlias(true);
        paint.setDither(true);
        paint.setFilterBitmap(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawOval(rectF, paint);

        paint.setColor(Color.BLUE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth((float) 4);
        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        return output;
    }
Schuman answered 18/7, 2014 at 11:20 Comment(0)
D
11

This can be easlity done in xml as well without cropping the actual bitmap, You just need to create a circular image mask and place over your actual image. Here is the piece of code which i used:

circle.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval" >
    <gradient android:startColor="#00FFFFFF" android:endColor="#00FFFFFF"
        android:angle="270"/>
     <stroke android:width="10dp" android:color="#FFAAAAAA"/>

your_layout.xml (Ignore "android:scaleType="fitXY"" if you don't need it)

<RelativeLayout

        android:id="@+id/icon_layout"
        android:layout_width="@dimen/icon_mask"
        android:layout_height="@dimen/icon_mask"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" >

        <ImageView
            android:id="@+id/icon"
            android:layout_width="@dimen/icon"
            android:layout_height="@dimen/icon"
            android:layout_centerInParent="true"
            android:scaleType="fitXY" >
        </ImageView>

        <ImageView
            android:id="@+id/icon_mask"
            android:layout_width="@dimen/icon_mask"
            android:layout_height="@dimen/icon_mask"
            android:layout_centerInParent="true"
            android:background="@drawable/circle"
            android:scaleType="fitXY" >
        </ImageView>
    </RelativeLayout>

dimen.xml


<dimen name="icon">36dp</dimen>
<dimen name="icon_mask">55dp</dimen>

enter image description here

OutPut Image View:

Hope, It might be useful for someone!!! :)

Darbee answered 17/8, 2013 at 10:55 Comment(3)
Seems like it's working only if image has a transparent background that is smaller than circle...Alwyn
You just put an ImageView on top of another, this isn't a mask :)Highsounding
@Darbee sure, you're right :) I's just that this way you don't really "crop" the original image, as asked by the original poster ;)Highsounding
W
8

you can use this code, it will work

 private Bitmap getCircleBitmap(Bitmap bitmap) {
        final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        final Canvas canvas = new Canvas(output);

        final int color = Color.RED;
        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(rect);

        paint.setAntiAlias(true);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(color);
        canvas.drawOval(rectF, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(bitmap, rect, rect, paint);

        bitmap.recycle();

        return output;
    }
Welldone answered 7/10, 2015 at 12:48 Comment(2)
It works well on Android API < 28, but crashes with java.lang.IllegalArgumentException: Software rendering doesn't support hardware bitmaps on Android 28Hamby
it's creating oval bitmap instead of circle.Seamstress
L
7

you can use this code, it will work

public Bitmap getRoundedShape(Bitmap scaleBitmapImage) {
    int targetWidth = 110;
    int targetHeight = 110;
    Bitmap targetBitmap = Bitmap.createBitmap(targetWidth, 
            targetHeight,Bitmap.Config.ARGB_8888);

    Canvas canvas = new Canvas(targetBitmap);
    Path path = new Path();
    path.addCircle(((float) targetWidth - 1) / 2,
            ((float) targetHeight - 1) / 2,
            (Math.min(((float) targetWidth), 
                    ((float) targetHeight)) / 2),
                    Path.Direction.CCW);

    canvas.clipPath(path);
    Bitmap sourceBitmap = scaleBitmapImage;
    canvas.drawBitmap(sourceBitmap, 
            new Rect(0, 0, sourceBitmap.getWidth(),
                    sourceBitmap.getHeight()), 
                    new Rect(0, 0, targetWidth, targetHeight), new Paint(Paint.FILTER_BITMAP_FLAG));
    return targetBitmap;
}
Liberality answered 7/7, 2014 at 15:0 Comment(1)
Unfortunately, clipPath is not antialiasingApophasis
G
4

I believe the easiest solution is to create a BitmapShader of your Bitmap, pass it to your paint object and then simply call something like canvas.drawCircle(cx, cy, radius, paint);

for example

Paint p = new Paint();
p.setShader(new BitmapShader(myBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
canvas.drawCircle(getWidth() / 2, getHeight() / 2, getHeight() / 2, p);

This is how https://github.com/hdodenhof/CircleImageView also has done it, you can read the source code here: https://github.com/hdodenhof/CircleImageView/blob/master/circleimageview/src/main/java/de/hdodenhof/circleimageview/CircleImageView.java

Glottal answered 14/1, 2019 at 15:35 Comment(0)
G
3

I recommend adding bitmap.recycle() if you don't need it anymore, it will prevent OutOfMemory error.

Gunny answered 15/7, 2014 at 9:36 Comment(0)
P
3

For peaple who want the center of the rectangle (me), add this before cutting:

    public static Bitmap cropBitmapToBlock(Bitmap bitmap) {
    if (bitmap.getWidth() >= bitmap.getHeight()){
        return Bitmap.createBitmap(
                bitmap,
                bitmap.getWidth()/2 - bitmap.getHeight()/2,
                0,
                bitmap.getHeight(),
                bitmap.getHeight()
        );
    }else{
        return Bitmap.createBitmap(
                bitmap,
                0,
                bitmap.getHeight()/2 - bitmap.getWidth()/2,
                bitmap.getWidth(),
                bitmap.getWidth()
        );
    }
} 

Android Crop Center of Bitmap

Pecksniffian answered 7/6, 2015 at 15:12 Comment(0)
P
3

Here is Kotlin variant using extension method

/**
 * Creates new circular bitmap based on original one.
 */
fun Bitmap.getCircularBitmap(config: Bitmap.Config = Bitmap.Config.ARGB_8888): Bitmap {
    // circle configuration
    val circlePaint = Paint().apply { isAntiAlias = true }
    val circleRadius = Math.max(width, height) / 2f

    // output bitmap
    val outputBitmapPaint = Paint(circlePaint).apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN) }
    val outputBounds = Rect(0, 0, width, height)
    val output = Bitmap.createBitmap(width, height, config)

    return Canvas(output).run {
        drawCircle(circleRadius, circleRadius, circleRadius, circlePaint)
        drawBitmap(this@getCircularBitmap, outputBounds, outputBounds, outputBitmapPaint)
        output
    }
}
Pursley answered 6/10, 2017 at 19:56 Comment(0)
G
2

Based on [Jachumbelechao Unto Mantekilla] answer, this code works like a charm for people looking for a Kotlin solution:

fun cropCircleFromBitmap(originalBitmap: Bitmap): Bitmap {
    val size = Math.min(originalBitmap.width, originalBitmap.height)
    val bitmap = ThumbnailUtils.extractThumbnail(originalBitmap, size, size)
    var output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)
    val paint = Paint()
    val rect = Rect(0, 0, bitmap.width, bitmap.height)
    val rectF = RectF(rect)
    paint.isAntiAlias = true
    paint.isDither = true
    paint.isFilterBitmap = true
    canvas.drawARGB(0, 0, 0, 0)
    paint.color = 0xffff0000.toInt()
    canvas.drawOval(rectF, paint)
    paint.color = Color.BLUE
    paint.style = Paint.Style.STROKE
    paint.strokeWidth = 4f
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
    canvas.drawBitmap(bitmap, rect, rect, paint)
    return output
}
Geller answered 26/5, 2016 at 16:31 Comment(4)
You could convert this into an extension functionRoadrunner
something like fun Bitmap.getCircleCroppedBitmap(): Bitmap {} and use this instead of originalBitmapRoadrunner
then you could use it like this: img_user_photo.setImageBitmap(photo.getCircleCroppedBitmap())Roadrunner
where photo is the bitmap object extended with the functionRoadrunner
E
2

Now, Right answer:

private Bitmap getCroppedBitmap(Bitmap bitmap, Integer cx, Integer cy, Integer radius) {
    int diam = radius << 1;
    Bitmap targetBitmap = Bitmap.createBitmap(diam, diam, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(targetBitmap);
    final int color = 0xff424242;
    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    canvas.drawARGB(0, 0, 0, 0);
    paint.setColor(color);
    canvas.drawCircle(radius, radius, radius, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
    canvas.drawBitmap(bitmap, -cx+radius, -cy+radius, paint);
    return targetBitmap;
}
Equanimous answered 24/4, 2017 at 16:51 Comment(2)
What are cx and cy?Lachesis
@K_7, this is the center of a circleEquanimous
S
1

Kotin Fucntion

 fun getRoundedCornerBitmap(bitmap: Bitmap, pixels: Int): Bitmap {
            val output = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888)
            val canvas = Canvas(output)

            val color = -0xbdbdbe
            val paint = Paint()
            val rect = Rect(0, 0, bitmap.width, bitmap.height)
            val rectF = RectF(rect)
            val roundPx = pixels.toFloat()

            paint.isAntiAlias = true
            canvas.drawARGB(0, 0, 0, 0)
            paint.color = color
            canvas.drawRoundRect(rectF, roundPx, roundPx, paint)

            paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
            canvas.drawBitmap(bitmap, rect, rect, paint)

            return output
        }

call it by this code

 holder.itemFriendImage.setImageBitmap(ImageConverter.getRoundedCornerBitmap(bitmap,600))
Sarsen answered 16/12, 2017 at 19:40 Comment(0)
S
0
**Jst Add this to your image Id and get the circuler image.**

 imgUserProfile.setImageBitmap(getCircularCenterCropBitmap(bitmap, (int) (150 * denisty)));

Method:-

public void Bitmap getCircularCenterCropBitmap(Bitmap originalBmp, int diameter) {
        Bitmap resizedBmp = BitmapUtils.getScaledCroppedBitmap(originalBmp, diameter, diameter);
        return BitmapUtils.getRoundedCircularBitmap(resizedBmp, diameter / 2);
    }
Shafting answered 24/12, 2018 at 11:18 Comment(1)
Omg. Thanks for your copypaste from some working project. One problem - the method is in the BitmapUtils file, which you forgot to postVertex
J
0

For kotlin:

private fun getCircularBitmap(bitmap: Bitmap): Bitmap? {
        val output = Bitmap.createBitmap(
            bitmap.width,
            bitmap.height, Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(output)
        val color = -0xbdbdbe
        val paint = Paint()
        val rect = Rect(0, 0, bitmap.width, bitmap.height)
        paint.isAntiAlias = true
        canvas.drawARGB(0, 0, 0, 0)
        paint.color = color
        canvas.drawCircle(
            (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat(), (
                    bitmap.width / 2).toFloat(), paint
        )
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
        canvas.drawBitmap(bitmap, rect, rect, paint)
        return output
    }
Januarius answered 11/10, 2021 at 4:53 Comment(0)
W
0

kotlin, put it in Ext.kt

 private fun Bitmap.getCircledBitmap(): Bitmap {
    val output = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
    val canvas = Canvas(output)
    val paint = Paint()
    val rect = Rect(0, 0, this.width, this.height)
    paint.isAntiAlias = true
    canvas.drawARGB(0, 0, 0, 0)
    canvas.drawCircle(this.width / 2f, this.height / 2f, this.width / 2f, paint)
    paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
    canvas.drawBitmap(this, rect, rect, paint)
    return output
}
Whittier answered 29/11, 2021 at 3:49 Comment(0)
E
-1

Not sure this is a programming question but...

The easiest solution would be to make the outside area transparent in the source bitmap. Otherwise, you'll have to calculate which pixels are outside of the circle, and set the alpha accordingly (alpha = 0 for full transparency).

Erlandson answered 13/8, 2012 at 19:16 Comment(2)
to be honest, I have been your way, it seems work but we cannot solve the border jaggy problem, don't you?Quixote
Border "jaggyness" is addressed by dithering and/or antialiasing. You could look online for some algorithms to accomplish something that seems acceptable. But keep in mind that rectangular pixels and curves will always have these issues.Erlandson

© 2022 - 2024 — McMap. All rights reserved.