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

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

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

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

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.


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

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 !!! :)


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

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

3

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


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

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


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
}

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

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

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


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

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


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
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.