So schneiden Sie kreisförmige Flächen aus Bitmap in Android

108

Ich habe eine Bitmap und möchte aus dieser Bitmap einen kreisförmigen Bereich zuschneiden. Alle Pixel außerhalb des Kreises sollten transparent sein. Wie kann ich das machen?

Geben Sie hier die Bildbeschreibung ein

Altaf
quelle

Antworten:

216

Nach langem Brainstorming habe ich die Lösung gefunden

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
quelle
1
Sie können dies auch tun, indem Sie die Bitmap gegen einen kreisförmigen Beschneidungspfad abschneiden. Sie können dies entweder jedes Mal tun, wenn Sie die Bitmap zeichnen, was bedeutet, dass Sie niemals eine Bitmap mit transparenten Pixeln erstellen würden, oder Sie können die abgeschnittene Bitmap in einen Puffer zeichnen, der zuvor auf transparent gelöscht wurde. Ich denke, beides wäre etwas schneller und einfacher.
Gene
1
Vielen Dank. Ihr Code funktioniert spektakulär. Jetzt kann ich auch mit Pfad (Polygon) zuschneiden.
DearDhruv
2
Diese Methode könnte staticin einer nicht instanziierten Utility-Klasse ähnlicher statischer Methoden erstellt und verwendet werden.
Matt Logan
1
Sollten Sie hier nicht ein Minimum an Höhe und Breite geteilt durch 2 als Radius verwenden? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
Varvara Kalinina
3
Es gibt 3 wichtige Punkte: 1) Erstellen Sie eine leere Bitmap und zeichnen Sie einen Kreis. 2) Setzen Sie xfermode auf SRC_IN. 3) Zeichnen Sie die echte Bitmap an diese Leinwandgrenzen. Die Farbe und andere Leinwandzeichnungen nützen also nichts.
Lym Zoy
44

Kreis aus Rechtecken erzeugen

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;
}
Diesel
quelle
1
Ich würde vorschlagen, zwei Bildansichten in einem Framelayout mit der oberen Bildansicht mit ausgeschnittenem transparentem Kreis zu verwenden.
Diesel
36

Mit RoundedBitmapDrawable können Sie Ihre Bildansicht kreisförmig gestalten

Hier ist der Code zum Erreichen von 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);
sree
quelle
Vielen Dank, dass Sie diese Option für die v4-Supportbibliothek angezeigt haben. Ich glaube nicht, dass ich es sonst herausgefunden hätte.
CFJ90210
8
und die Verwendung von setCircular (true) anstelle von setCornerRadius (50.0f) macht das Zeichnen zu einem Kreis. Hinweis: Das Bild muss quadratisch sein oder das Seitenverhältnis ist fehlerhaft ...
Marco Schmitz
31

@Gene hat die obige Antwort kommentiert und vorgeschlagen, clipPathein Bild als Kreis zuzuschneiden .

Das Folgende ist eine saubere Implementierung davon:

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

Dies könnte einer Utility-Klasse hinzugefügt werden.

HGPB
quelle
4
Ich wollte gerade einen sehr ähnlichen Code posten. Das Problem ist laut developer.android.com/guide/topics/graphics/hardware-accel.html , clipPath wird bei der Hardwarebeschleunigung nicht unterstützt. Ich bin tatsächlich in einer App auf dieses Problem gestoßen und habe mich gefragt, was los ist. Neuere Hardware scheint dies jedoch zu beheben (wie Google Tablets). Eine mögliche weitere Bereinigung Ihres Codes: Sie benötigen keine Rect-to-Rect-Konvertierung, wenn Sie die Bitmap zeichnen. Sie können einfach sagen c.drawBitmap(b, 0, 0, null);, welche die Standardidentitätstransformation verwendet.
Gene
Wie sind Sie mit clipPath bei der Hardwarebeschleunigung umgegangen?
Speedynomads
Ich habe diese Lösung ursprünglich verwendet, aber die Ausgabe hatte gezackte Kanten. Die Lösung von @Altaf funktioniert besser
Dirkoneill
Funktioniert hervorragend zum Zuschneiden von Bildern, die in der Benachrichtigung in der Statusleiste verwendet werden
Damian Petla,
14

Ich denke, diese Lösung funktioniert besser mit jeder Art von Rechteck. Ändern Sie die Pixelgröße, wenn Sie ein kleines oder großes Bild wünschen:

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 nach Mantekilla
quelle
11

Dies kann auch in XML problemlos durchgeführt werden, ohne dass die eigentliche Bitmap beschnitten wird. Sie müssen lediglich eine kreisförmige Bildmaske erstellen und über Ihrem tatsächlichen Bild platzieren. Hier ist der Code, den ich verwendet habe:

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 (Ignoriere "android: scaleType =" fitXY "", wenn du es nicht brauchst)

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

Geben Sie hier die Bildbeschreibung ein

OutPut-Bildansicht:

Hoffe, es könnte für jemanden nützlich sein !!! :) :)

Asche
quelle
Scheint, als würde es nur funktionieren, wenn das Bild einen transparenten Hintergrund hat, der kleiner als der Kreis ist ...
meeDamian
4
Sie setzen einfach eine ImageView auf eine andere, dies ist keine Maske :)
Climbatize
@Ash sicher, du hast Recht :) Ich bin nur so, dass du auf diese Weise das Originalbild nicht wirklich "beschneidest", wie es das Originalplakat verlangt;)
Climbatize
8

Sie können diesen Code verwenden, es wird funktionieren

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

quelle
Es funktioniert gut auf Android API <28, stürzt jedoch mit java.lang.IllegalArgumentException ab: Software-Rendering unterstützt keine Hardware-Bitmaps auf Android 28
Lucian Iacob
7

Sie können diesen Code verwenden, es wird funktionieren

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

Ich empfehle das Hinzufügen, bitmap.recycle()wenn Sie es nicht mehr benötigen, es wird OutOfMemory-Fehler verhindern.

Badoualy
quelle
3

Hier ist die Kotlin-Variante mit Erweiterungsmethode

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

Für Leute, die die Mitte des Rechtecks ​​(mich) haben möchten, fügen Sie dies vor dem Schneiden hinzu:

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

jobbert
quelle
2

Basierend auf der Antwort von [Jachumbelechao Unto Mantekilla] wirkt dieser Code wie ein Zauber für Leute, die nach einer Kotlin-Lösung suchen:

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
quelle
Sie könnten dies in eine Erweiterungsfunktion umwandeln
greenspand
so etwas wie Spaß Bitmap.getCircleCroppedBitmap (): Bitmap {} und verwenden Sie diese anstelle von originalBitmap
greenspand
img_user_photo.setImageBitmap (photo.getCircleCroppedBitmap ()): dann könnte man es wie diese verwenden
greenspand
Dabei ist Foto das Bitmap-Objekt, das mit der Funktion erweitert wurde
Greenspand
1

Nun, richtige Antwort:

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;
}
Meister
quelle
1
Was sind cx und cy?
Kartik Chugh
1
@ K_7, dies ist der Mittelpunkt eines Kreises
Meister
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
        }

Nennen Sie es mit diesem Code

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

Ich glaube, die einfachste Lösung besteht darin, einen BitmapShader Ihrer Bitmap zu erstellen, ihn an Ihr Malobjekt zu übergeben und dann einfach so etwas wie canvas.drawCircle (cx, cy, radius, paint) aufzurufen.

beispielsweise

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

So hat es auch https://github.com/hdodenhof/CircleImageView gemacht. Den Quellcode können Sie hier lesen: https://github.com/hdodenhof/CircleImageView/blob/master/circleimageview/src/main/java /de/hdodenhof/circleimageview/CircleImageView.java

Martin Nowosad
quelle
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
quelle
-1

Ich bin mir nicht sicher, ob dies eine Programmierfrage ist, aber ...

Die einfachste Lösung wäre, den Außenbereich in der Quellbitmap transparent zu machen. Andernfalls müssen Sie berechnen, welche Pixel außerhalb des Kreises liegen, und das Alpha entsprechend einstellen (Alpha = 0 für volle Transparenz).

MandisaW
quelle
um ehrlich zu sein, ich war dein Weg, es scheint Arbeit zu sein, aber wir können das Grenzproblem nicht lösen, nicht wahr?
VinceStyling
Grenzzacke wird durch Dithering und / oder Antialiasing angegangen. Sie könnten online nach einigen Algorithmen suchen, um etwas zu erreichen, das akzeptabel erscheint. Beachten Sie jedoch, dass rechteckige Pixel und Kurven immer diese Probleme haben.
MandisaW