Cách cắt vùng tròn từ ảnh bitmap trong Android


108

Tôi có một bitmap và tôi muốn cắt một vùng hình tròn từ bitmap này. Tất cả các pixel bên ngoài vòng tròn phải trong suốt. Tôi có thể làm cái này như thế nào?

nhập mô tả hình ảnh ở đây

Câu trả lời:


216

Sau thời gian dài cân não, tôi đã tìm ra giải pháp

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;
}

1
Bạn cũng có thể làm điều này bằng cách cắt bitmap theo đường cắt hình tròn. Bạn có thể làm điều này mỗi khi bạn vẽ bitmap, có nghĩa là bạn sẽ không bao giờ thực sự tạo một bitmap với các pixel trong suốt hoặc bạn có thể vẽ bitmap đã cắt vào một bộ đệm đã được xóa thành trong suốt trước đó. Tôi nghĩ rằng một trong hai sẽ nhanh hơn và đơn giản hơn một chút.
Gene

1
Cảm ơn. mã của bạn hoạt động ngoạn mục. Bây giờ tôi cũng có thể cắt bằng đường dẫn (Đa giác).
DearDhruv 21/09/13

2
Phương thức này có thể được tạo staticvà sử dụng trong một lớp tiện ích không khởi tạo của các phương thức tĩnh tương tự.
Matt Logan

1
Bạn không nên sử dụng tối thiểu chiều cao và chiều rộng chia cho 2 làm bán kính ở đây? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
Varvara Kalinina

3
Có 3 điểm chính: 1) Tạo một bitmap trống và vẽ một vòng tròn. 2) Đặt xfermode thành SRC_IN. 3) Vẽ bitmap thực đến giới hạn canvas đó. Vì vậy, màu sơn và các bản vẽ canvas khác không có ích lợi gì.
Lym Zoy

44

để tạo Vòng tròn từ các hình chữ nhật

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;
}

1
tôi sẽ đề xuất sử dụng hai chế độ xem hình ảnh trong một framelayout với chế độ xem hình ảnh trên cùng với hình tròn trong suốt được cắt ra.
diesel

36

Bạn có thể làm cho chế độ xem hình ảnh của mình hình tròn bằng cách sử dụng RoundedBitmapDrawable

đây là mã để đạt được roundImageview:

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);

Cảm ơn bạn đã hiển thị tùy chọn thư viện Hỗ trợ v4 này. Tôi không nghĩ rằng tôi sẽ tìm hiểu về nó nếu không.
CFJ90210

8
và sử dụng setCircular (true) thay vì setCornerRadius (50.0f) làm cho hình tròn có thể vẽ được. Lưu ý: hình ảnh phải là hình vuông hoặc tỉ lệ khung hình là khiếm khuyết ...
Marco Schmitz

31

@Gene đã đưa ra nhận xét về câu trả lời ở trên và đề xuất sử dụng clipPathlàm tùy chọn để cắt hình ảnh dưới dạng hình tròn.

Sau đây là một triển khai rõ ràng của điều này:

    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;
    }

Điều này có thể được thêm vào một lớp tiện ích.


4
Tôi vừa định đăng mã rất giống nhau. Vấn đề là theo developer.android.com/guide/topics/graphics/hardware-accel.html , clipPath không được hỗ trợ tăng tốc phần cứng. Tôi thực sự gặp phải vấn đề đó trong một ứng dụng và tự hỏi điều gì đang xảy ra. Tuy nhiên, phần cứng mới hơn dường như khắc phục được điều này (như máy tính bảng của Google). Một khả năng có thể làm sạch thêm cho mã của bạn: Bạn không cần chuyển đổi trực tiếp thành trực tràng khi vẽ bitmap. Bạn chỉ có thể nói c.drawBitmap(b, 0, 0, null);, sử dụng biến đổi danh tính mặc định.
Gene

bạn đã sử dụng clipPath như thế nào trong khi tăng tốc phần cứng?
speedynomads

Tôi ban đầu đã sử dụng giải pháp này trước đây nhưng đầu ra có các cạnh răng cưa. Các giải pháp từ @Altaf hoạt động tốt hơn
dirkoneill

Hoạt động tuyệt vời cho hình ảnh cắt xén được sử dụng trong thông báo trên thanh trạng thái
Damian Petla

14

Tôi nghĩ giải pháp này hoạt động tốt hơn với bất kỳ loại hình chữ nhật nào, hãy thay đổi kích thước pixel nếu bạn muốn hình ảnh nhỏ hoặc lớn:

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;
    }

11

Điều này có thể được thực hiện trong xml mà không cần cắt ảnh bitmap thực tế, Bạn chỉ cần tạo một mặt nạ hình ảnh tròn và đặt lên hình ảnh thực tế của bạn. Đây là đoạn mã mà tôi đã sử dụng:

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 (Bỏ qua "android: scaleType =" fitXY "" nếu bạn không cần)

<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>

nhập mô tả hình ảnh ở đây

Chế độ xem hình ảnh OutPut:

Hy vọng, nó có thể hữu ích cho ai đó !!! :)


Có vẻ như nó hoạt động chỉ khi hình ảnh có nền trong suốt mà là nhỏ hơn so với vòng tròn ...
meeDamian

4
Bạn chỉ cần đặt một ImageView trên đầu trang của người khác, đây không phải là một chiếc mặt nạ :)
Climbatize

@Ash chắc, bạn nói đúng :) Tôi chỉ là theo cách này bạn không thực sự "cắt" hình ảnh gốc, như người đăng ban đầu đã yêu cầu;)
Climbatize

8

bạn có thể sử dụng mã này, nó sẽ hoạt động

 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;
    }

Nó hoạt động tốt trên API Android <28, nhưng bị treo với java.lang.IllegalArgumentException: Phần mềm vẽ không hỗ trợ bitmap phần cứng trên Android 28
Lucian Iacob

7

bạn có thể sử dụng mã này, nó sẽ hoạt động

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;
}

3

Tôi khuyên bạn nên thêm bitmap.recycle()nếu bạn không cần nó nữa, nó sẽ ngăn lỗi OutOfMemory.


3

Đây là biến thể Kotlin sử dụng phương thức mở rộng

/**
 * 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
    }
}

2

Đối với những người muốn có tâm của hình chữ nhật (tôi), hãy thêm cái này trước khi cắt:

    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()
        );
    }
} 

Trung tâm ảnh bitmap của Android Crop


2

Dựa trên câu trả lời của [Jachumbelechao Unto Mantekilla], mã này hoạt động giống như một sự quyến rũ cho những người đang tìm kiếm giải pháp Kotlin:

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
}

Bạn có thể chuyển đổi này vào một chức năng mở rộng
greenspand

một cái gì đó giống như Bitmap.getCircleCroppedBitmap (): Bitmap {} và sử dụng cái này thay vì originalBitmap
greenspand

thì bạn có thể sử dụng nó như thế này: img_user_photo.setImageBitmap (photo.getCircleCroppedBitmap ())
greenspand

nơi ảnh là đối tượng bitmap mở rộng với chức năng
greenspand

1

Bây giờ, câu trả lời đúng:

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;
}

1
Cx và cy là gì?
Kartik Chugh

1
@ K_7, đây là tâm của một vòng tròn
Master

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
        }

gọi nó bằng mã này

 holder.itemFriendImage.setImageBitmap(ImageConverter.getRoundedCornerBitmap(bitmap,600))

1

Tôi tin rằng giải pháp đơn giản nhất là tạo một BitmapShader của Bitmap của bạn, chuyển nó vào đối tượng paint của bạn và sau đó chỉ cần gọi một cái gì đó như canvas.drawCircle (cx, cy, radius, paint);

ví dụ

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

Đây là cách https://github.com/hdodenhof/CircleImageView cũng đã thực hiện, bạn có thể đọc mã nguồn tại đây: https://github.com/hdodenhof/CircleImageView/blob/master/circleimageview/src/main/java /de/hdodenhof/circleimageview/CircleImageView.java


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);
    }

-1

Không chắc đây là một câu hỏi lập trình nhưng ...

Giải pháp dễ nhất là làm cho khu vực bên ngoài trong suốt trong bitmap nguồn. Nếu không, bạn sẽ phải tính toán những pixel nào nằm ngoài vòng tròn và đặt alpha cho phù hợp (alpha = 0 để có độ trong suốt hoàn toàn).


thành thật mà nói, tôi đã theo cách của bạn, nó có vẻ hiệu quả nhưng chúng tôi không thể giải quyết vấn đề biên giới lởm chởm, phải không?
VinceStyling

Đường viền "lởm chởm" được giải quyết bằng cách phối màu và / hoặc khử răng cưa. Bạn có thể tìm kiếm một số thuật toán trực tuyến để thực hiện điều gì đó có vẻ chấp nhận được. Nhưng hãy nhớ rằng các pixel và đường cong hình chữ nhật sẽ luôn có những vấn đề này.
MandisaW
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.