Android'de bitmap'ten dairesel alan nasıl kırpılır


Yanıtlar:


216

Uzun beyin fırtınasından sonra çözümü buldum

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
Bunu, bitmap'i dairesel bir kırpma yoluna doğru kırparak da yapabilirsiniz. Bitmap'i her çizdiğinizde bunu yapabilirsiniz, bu da aslında saydam piksellerle bir bitmap oluşturmayacağınız anlamına gelir veya kırpılmış bitmap'i önceden saydam olacak şekilde silinmiş bir arabelleğe çizebilirsiniz. Sanırım ya bundan biraz daha hızlı ve basit olurdu.
Gene

1
Teşekkürler. kodunuz muhteşem çalışıyor. Artık yolu (Çokgen) kullanarak da kırpabiliyorum.
DearDhruv

2
Bu yöntem static, benzer statik yöntemlerin örneklenmemiş bir fayda sınıfında yapılabilir ve kullanılabilir.
Matt Logan

1
Burada minimum yükseklik ve genişliğin 2'ye bölünmesini yarıçap olarak kullanmanız gerekmez mi? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
Varvara Kalinina

3
3 önemli nokta vardır: 1) Boş bir bitmap oluşturun ve bir daire çizin. 2) xfermode'u SRC_IN olarak ayarlayın. 3) Gerçek bitmap'i bu tuval sınırlarına çizin. Bu nedenle, boya rengi ve diğer tuval çizimlerinin hiçbir faydası yoktur.
Lym Zoy

44

dikdörtgenlerden Çember oluşturmak için

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
Şeffaf daire kesilmiş üst görüntü görünümü ile bir çerçeve düzeninde iki görüntü görüntülemeyi kullanmanızı öneririm.
dizel

36

RoundedBitmapDrawable kullanarak görüntü görünümünüzü dairesel hale getirebilirsiniz

roundedImageview'a ulaşmak için kod:

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

Bu v4 Destek kitaplığı seçeneğini gösterdiğiniz için teşekkür ederiz. Aksi takdirde bunu öğreneceğimi sanmıyorum.
CFJ90210

8
ve setCornerRadius (50.0f) yerine setCircular (true) kullanmak, çizilebilirliği bir daire haline getirir. not: resim kare olmalı veya en boy oranı bozuk ...
Marco Schmitz

31

@Gene, clipPathbir resmi daire şeklinde kırpmak için bir seçenek olarak kullanılmasını öneren yukarıdaki yanıta bir yorum yaptı .

Aşağıdakiler bunun temiz bir uygulamasıdır:

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

Bu, bir fayda sınıfına eklenebilir.


4
Tam da çok benzer bir kod göndermek üzereydim. Sorun, developer.android.com/guide/topics/graphics/hardware-accel.html'ye göre , clipPath donanım hızlandırma ile desteklenmiyor. Aslında bir uygulamada bu problemle karşılaştım ve neler olduğunu merak ettim. Ancak daha yeni donanımlar bunu düzeltir gibi görünüyor (Google tabletler gibi). Kodunuz için bir başka olası temizleme daha: Bitmap'i çizerken doğrudan dikene dönüştürmeye ihtiyacınız yoktur. Sadece c.drawBitmap(b, 0, 0, null);varsayılan kimlik dönüşümünü kullanan diyebilirsiniz .
Gene

donanım hızlandırmayı kullanırken clipPath kullanarak nasıl dolaştınız?
speedynomads

Başlangıçta bu çözümü daha önce kullanıyordum ancak çıktının kenarları pürüzlü. @Altaf'ın çözümü daha iyi çalışıyor
dirkoneill

Durum çubuğundaki bildirimde kullanılan görüntüleri kırpmak için harika çalışıyor
Damian Petla

14

Bu çözümün her tür dikdörtgenle daha iyi çalıştığını düşünüyorum, küçük veya büyük görüntü istiyorsanız piksel boyutunu değiştirin:

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

Bu, gerçek bitmap'i kırpmadan xml'de de kolaylıkla yapılabilir. Sadece dairesel bir görüntü maskesi oluşturmanız ve gerçek görüntünüzün üzerine yerleştirmeniz gerekir. İşte kullandığım kod parçası:

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 ( İhtiyacınız yoksa "android: scaleType =" fitXY "" seçeneğini yoksayı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>

görüntü açıklamasını buraya girin

OutPut Resim Görünümü:

Umut, birisi için yararlı olabilir !!! :)


Görünüşe göre, yalnızca görüntü daireden daha küçük şeffaf bir arka plana
sahipse işe yarıyor

4
Bir ImageView'u diğerinin üzerine koydunuz, bu bir maske değil :)
Climbatize

@ Kesinlikle, haklısın :) Sadece bu şekilde, orijinal
göndericinin

8

bu kodu kullanabilirsin, işe yarayacak

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

Android API <28'de iyi çalışıyor, ancak java.lang.IllegalArgumentException ile çöküyor: Yazılım işleme, Android 28'de donanım bitmap'lerini desteklemiyor
Lucian Iacob

7

bu kodu kullanabilirsin, işe yarayacak

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

bitmap.recycle()Artık ihtiyacınız yoksa eklemenizi öneririm , OutOfMemory hatasını önleyecektir.


3

İşte uzantı yöntemini kullanan Kotlin varyantı

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

Dikdörtgenin merkezini (ben) isteyenler için, kesmeden önce şunu ekleyin:

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

Bitmap'in Android Kırpma Merkezi


2

[Jachumbelechao Unto Mantekilla] cevabına göre, bu kod bir Kotlin çözümü arayan insanlar için bir cazibe işlevi görür:

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
}

Bunu bir uzantı işlevine dönüştürebilirsiniz
greenspand

eğlenceli bir şey Bitmap.getCircleCroppedBitmap (): Bitmap {} ve originalBitmap yerine bunu kullanın
greenspand

o zaman bunu şu şekilde kullanabilirsiniz: img_user_photo.setImageBitmap (photo.getCircleCroppedBitmap ())
greenspand

fotoğraf, işlevle genişletilen bitmap nesnesi
greenspand

1

Şimdi, Doğru cevap:

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 ve cy nedir?
Kartik Chugh

1
@ K_7, burası bir çemberin merkezi
Master

1

Kotin İşlevi

 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
        }

bu kodla çağır

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

1

En kolay çözümün Bitmap'inizin bir BitmapShader'ını oluşturmak, onu boyama nesnenize aktarmak ve ardından canvas.drawCircle (cx, cy, radius, paint) gibi bir şey çağırmak olduğuna inanıyorum;

Örneğin

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

Bu nasıl https://github.com/hdodenhof/CircleImageView burada kaynak kodunu okuyabilir, ayrıca yapacağını yaptı: 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

Bunun bir programlama sorusu olduğundan emin değilim ama ...

En kolay çözüm, kaynak bit eşleminde dış alanı saydam yapmaktır. Aksi takdirde, hangi piksellerin dairenin dışında olduğunu hesaplamanız ve buna göre alfa ayarlamanız gerekir (tam şeffaflık için alfa = 0).


Dürüst olmak gerekirse, senin yöntemindeydim, işe yarıyor gibi görünüyor ama sınır jaggy sorununu çözemiyoruz, değil mi?
VinceStyling

Sınır "jaggyness", titreme ve / veya antialiasing ile giderilir. Kabul edilebilir görünen bir şeyi başarmak için bazı algoritmalar için internete bakabilirsiniz. Ancak, dikdörtgen piksellerin ve eğrilerin her zaman bu sorunları yaşayacağını unutmayın.
MandisaW
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.