Cara memotong area melingkar dari bitmap di Android

108

Saya memiliki bitmap dan saya ingin memotong wilayah melingkar dari bitmap ini. Semua piksel di luar lingkaran harus transparan. Bagaimana saya bisa melakukan ini?

masukkan deskripsi gambar di sini

Altaf
sumber

Jawaban:

216

Setelah bertukar pikiran panjang, saya menemukan solusinya

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;
}
Altaf
sumber
1
Anda juga dapat melakukan ini dengan memotong bitmap pada jalur pemotongan melingkar. Anda dapat melakukan ini setiap kali Anda menggambar bitmap, yang berarti Anda tidak akan pernah benar-benar membuat bitmap dengan piksel transparan, atau Anda dapat menggambar bitmap yang terpotong ke dalam buffer yang telah dihapus menjadi transparan sebelumnya. Saya pikir keduanya akan sedikit lebih cepat dan lebih sederhana dari ini.
Gene
1
Terima kasih. kode Anda bekerja secara spektakuler. Sekarang saya juga bisa memotong menggunakan jalur (Polygon).
DearDhruv
2
Metode ini dapat dibuat staticdan digunakan dalam kelas utilitas non-instantiated dari metode statis serupa.
Matt Logan
1
Bukankah sebaiknya Anda menggunakan tinggi dan lebar minimum dibagi 2 sebagai jari-jari di sini? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
Varvara Kalinina
3
Ada 3 poin kunci: 1) Buat bitmap kosong dan gambar lingkaran. 2) Setel xfermode ke SRC_IN. 3) Gambarkan bitmap nyata ke batas kanvas itu. Jadi, warna cat dan gambar kanvas lainnya tidak ada gunanya.
Lym Zoy
44

untuk menghasilkan Lingkaran dari persegi panjang

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;
}
solar
sumber
1
saya akan menyarankan menggunakan dua tampilan gambar dalam framelayout dengan tampilan gambar atas dengan lingkaran transparan dipotong.
diesel
36

Anda dapat membuat imageview Anda melingkar menggunakan RoundedBitmapDrawable

berikut adalah kode untuk mencapai 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);
sree
sumber
Terima kasih telah menampilkan opsi pustaka Dukungan v4 ini. Saya tidak berpikir saya akan mengetahuinya sebaliknya.
CFJ90210
8
dan menggunakan setCircular (true) sebagai ganti setCornerRadius (50.0f) membuat drawable menjadi lingkaran. catatan: gambar harus persegi atau rasio aspeknya rusak ...
Marco Schmitz
31

@Gene mengomentari jawaban di atas yang menyarankan penggunaan clipPathsebagai opsi untuk memotong gambar sebagai lingkaran.

Berikut ini adalah implementasi bersih dari ini:

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

Ini dapat ditambahkan ke kelas utilitas.

HGPB
sumber
4
Saya baru saja akan memposting kode yang sangat mirip. Masalahnya, menurut developer.android.com/guide/topics/graphics/hardware-accel.html , clipPath tidak didukung dengan akselerasi perangkat keras. Saya benar-benar mengalami masalah itu di aplikasi dan bertanya-tanya apa yang sedang terjadi. Namun, perangkat keras yang lebih baru tampaknya memperbaiki ini (seperti tablet Google). Satu kemungkinan pembersihan lebih lanjut untuk kode Anda: Anda tidak memerlukan konversi rect-to-rect saat menggambar bitmap. Anda bisa mengatakan c.drawBitmap(b, 0, 0, null);, yang menggunakan transformasi identitas default.
Gene
bagaimana Anda menggunakan clipPath saat menggunakan akselerasi perangkat keras?
speedynomads
Saya awalnya menggunakan solusi ini sebelumnya tetapi hasilnya memiliki tepi yang bergerigi. Solusi dari @Altaf bekerja lebih baik
dirkoneill
Berfungsi bagus untuk memotong gambar yang digunakan dalam pemberitahuan pada bilah status
Damian Petla
14

Saya pikir solusi ini berfungsi lebih baik dengan semua jenis persegi panjang, ubah ukuran piksel jika Anda ingin gambar kecil atau besar:

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;
    }
Jachumbelechao Sampai Mantekilla
sumber
11

Ini dapat dengan mudah dilakukan dalam xml juga tanpa memotong bitmap yang sebenarnya, Anda hanya perlu membuat topeng gambar melingkar dan menempatkannya di atas gambar Anda yang sebenarnya. Berikut adalah potongan kode yang saya gunakan:

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 (Abaikan "android: scaleType =" fitXY "" jika Anda tidak membutuhkannya)

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

masukkan deskripsi gambar di sini

Tampilan Gambar OutPut:

Semoga bermanfaat bagi seseorang !!! :)

Abu
sumber
Sepertinya ini hanya berfungsi jika gambar memiliki latar belakang transparan yang lebih kecil dari lingkaran ...
meeDamian
4
Anda hanya meletakkan ImageView di atas yang lain, ini bukan topeng :)
Climbatize
@Ash yakin, Anda benar :) Saya hanya dengan cara ini Anda tidak benar-benar "memotong" gambar asli, seperti yang diminta oleh poster asli;)
Climbatize
8

Anda dapat menggunakan kode ini, itu akan berhasil

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

sumber
Ini berfungsi dengan baik di Android API <28, tetapi mogok dengan java.lang.IllegalArgumentException: Perenderan perangkat lunak tidak mendukung bitmap perangkat keras di Android 28
Lucian Iacob
7

Anda dapat menggunakan kode ini, itu akan berhasil

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;
}
anupam sharma
sumber
3

Saya sarankan menambahkan bitmap.recycle()jika Anda tidak membutuhkannya lagi, itu akan mencegah kesalahan OutOfMemory.

Badoualy
sumber
3

Berikut adalah varian Kotlin yang menggunakan metode ekstensi

/**
 * 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
    }
}
Yuriy Seredyuk
sumber
2

Untuk orang yang menginginkan bagian tengah persegi panjang (saya), tambahkan ini sebelum memotong:

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

jobbert
sumber
2

Berdasarkan jawaban [Jachumbelechao Unto Mantekilla], kode ini berfungsi seperti pesona bagi orang yang mencari solusi 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
}
Rodrigo Salinas
sumber
Anda dapat mengubahnya menjadi fungsi ekstensi
greenspand
sesuatu seperti Bitmap.getCircleCroppedBitmap (): Bitmap {} dan gunakan ini sebagai ganti originalBitmap
greenspand
maka Anda dapat menggunakannya seperti ini: img_user_photo.setImageBitmap (photo.getCircleCroppedBitmap ())
greenspand
di mana foto adalah objek bitmap yang diperluas dengan fungsi
greenspand
1

Sekarang, jawaban yang benar:

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;
}
Menguasai
sumber
1
Apa itu cx dan cy?
Kartik Chugh
1
@ K_7, ini adalah pusat lingkaran
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
        }

sebut saja dengan kode ini

 holder.itemFriendImage.setImageBitmap(ImageConverter.getRoundedCornerBitmap(bitmap,600))
Samet ÖZTOPRAK
sumber
1

Saya yakin solusi termudah adalah membuat BitmapShader dari Bitmap Anda, meneruskannya ke objek paint Anda dan kemudian cukup memanggil sesuatu seperti canvas.drawCircle (cx, cy, radius, paint);

sebagai contoh

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

Beginilah cara https://github.com/hdodenhof/CircleImageView juga melakukannya, Anda dapat membaca kode sumbernya di sini: https://github.com/hdodenhof/CircleImageView/blob/master/circleimageview/src/main/java /de/hdodenhof/circleimageview/CircleImageView.java

Martin Nowosad
sumber
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);
    }
Alok Singh
sumber
-1

Tidak yakin ini adalah pertanyaan pemrograman, tetapi ...

Solusi termudah adalah membuat area luar transparan di bitmap sumber. Jika tidak, Anda harus menghitung piksel mana yang berada di luar lingkaran, dan menyetel alfa yang sesuai (alfa = 0 untuk transparansi penuh).

MandisaW
sumber
Sejujurnya, saya telah mengikuti cara Anda, tampaknya berhasil tetapi kita tidak dapat menyelesaikan masalah jaggy perbatasan, bukan?
VinceStyling
Batas "jaggyness" diatasi dengan dithering dan / atau antialiasing. Anda dapat mencari beberapa algoritme online untuk mencapai sesuatu yang tampaknya dapat diterima. Namun perlu diingat bahwa piksel persegi dan kurva akan selalu memiliki masalah ini.
MandisaW