Android의 비트 맵에서 원형 영역을 자르는 방법


108

비트 맵이 있고이 비트 맵에서 원형 영역을 자르고 싶습니다. 원 밖의 모든 픽셀은 투명해야합니다. 어떻게 할 수 있습니까?

여기에 이미지 설명 입력

답변:


216

오랜 브레인 스토밍 끝에 해결책을 찾았습니다

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
원형 클리핑 경로에 대해 비트 맵을 클리핑하여이 작업을 수행 할 수도 있습니다. 비트 맵을 그릴 때마다이 작업을 수행 할 수 있습니다. 즉, 실제로는 투명한 픽셀이있는 비트 맵을 만들지 않았거나 미리 투명하게 지워진 버퍼에 잘린 비트 맵을 그릴 수 있습니다. 어느 쪽이든 이것보다 조금 더 빠르고 간단 할 것이라고 생각합니다.
Gene

1
감사. 코드가 훌륭하게 작동합니다. 이제 경로 (다각형)를 사용하여자를 수도 있습니다.
DearDhruv

2
이 메서드 static는 유사한 정적 메서드의 인스턴스화되지 않은 유틸리티 클래스에서 만들고 사용할 수 있습니다 .
Matt Logan

1
여기서 반경으로 최소 높이와 너비를 2로 나눈 값을 사용하면 안 되나요? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
Varvara Kalinina

3
세 가지 요점이 있습니다. 1) 빈 비트 맵을 만들고 원을 그립니다. 2) xfermode를 SRC_IN으로 설정합니다. 3) 캔버스 경계에 실제 비트 맵을 그립니다. 따라서 페인트 색상 및 기타 캔버스 드로잉은 소용이 없습니다.
Lym Zoy

44

직사각형에서 원을 생성하려면

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
투명한 원이 잘린 상단 이미지 뷰와 함께 프레임 레이아웃에서 두 개의 이미지 뷰를 사용하는 것이 좋습니다.
diesel

36

RoundedBitmapDrawable을 사용하여 이미지 뷰를 원형으로 만들 수 있습니다.

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

이 v4 지원 라이브러리 옵션을 보여 주셔서 감사합니다. 다른 방법으로는 알 수 없었을 것입니다.
CFJ90210

8
setCornerRadius (50.0f) 대신 setCircular (true)를 사용하면 드로어 블이 원이됩니다. 참고 : 이미지는 정사각형이거나 가로 세로 비율은 ... 결함이
마르코 슈미츠

31

@Gene은 위의 답변에 clipPath대해 이미지를 원으로 자르는 옵션으로 사용하도록 제안했습니다 .

다음은 이것의 깨끗한 구현입니다.

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

이것은 유틸리티 클래스에 추가 될 수 있습니다.


4
매우 유사한 코드를 게시하려고했습니다. 문제는 developer.android.com/guide/topics/graphics/hardware-accel.html 에 따르면 clipPath는 하드웨어 가속에서 지원되지 않습니다. 나는 실제로 앱에서 그 문제를 만났고 무슨 일이 일어나고 있는지 궁금했습니다. 그러나 최신 하드웨어가이 문제를 해결하는 것 같습니다 (예 : Google 태블릿). 코드를 추가로 정리할 수있는 한 가지 방법 : 비트 맵을 그릴 때 rect-to-rect 변환이 필요하지 않습니다. c.drawBitmap(b, 0, 0, null);기본 ID 변환을 사용하는 라고 말할 수 있습니다 .
유전자

하드웨어 가속을 사용하면서 어떻게 clipPath를 사용하게 되었습니까?
speedynomads

이전에이 솔루션을 사용하고 있었지만 출력에 가장자리가 들쭉날쭉했습니다. @Altaf의 솔루션이 더 잘 작동합니다
dirkoneill 2014 년

상태 표시 줄에 알림에 사용 자르기 이미지에 큰 작품
데미안 Petla

14

이 솔루션은 모든 유형의 직사각형에서 더 잘 작동한다고 생각하며 이미지를 작거나 크게 원한다면 픽셀 크기를 변경하십시오.

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

이것은 실제 비트 맵을 자르지 않고도 xml에서 쉽게 수행 할 수 있습니다. 원형 이미지 마스크를 만들고 실제 이미지 위에 배치하기 만하면됩니다. 다음은 내가 사용한 코드입니다.

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 (필요하지 않은 경우 "android : scaleType ="fitXY ""무시)

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

여기에 이미지 설명 입력

출력 이미지보기 :

희망, 누군가에게 유용 할 수 있습니다 !!! :)


이 ... 이미지가 원보다 작은 투명한 배경이있는 경우에만 작동하는 것처럼 보인다
meeDamian

4
당신은,이 :) 마스크 아닙니다 위에 다른 이미지 뷰를 넣어
Climbatize

@Ash, 당신이 맞아요 :) 원본 포스터에서 요청한대로 원본 이미지를 실제로 "자르지"않습니다.)
Climbatize

8

이 코드를 사용할 수 있습니다.

 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에서는 잘 작동하지만 java.lang.IllegalArgumentException과 충돌합니다. 소프트웨어 렌더링은 Android 28에서 하드웨어 비트 맵을 지원하지 않습니다
Lucian Iacob

7

이 코드를 사용할 수 있습니다.

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()당신이 더 이상 필요하지 않은 경우, 그것은에서 OutOfMemory 오류를 방지 할 수 있습니다.


3

다음은 확장 방법을 사용하는 Kotlin 변형입니다.

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

직사각형 (나)의 중심을 원하는 공작의 경우 자르기 전에 다음을 추가하십시오.

    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 자르기 센터


2

[Jachumbelechao Unto Mantekilla] 답변에 따르면이 코드는 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
}

이것을 확장 함수로 변환 할 수 있습니다
greenspand

재미있는 Bitmap.getCircleCroppedBitmap () : Bitmap {} 및 originalBitmap 대신 이것을 사용
greenspand

img_user_photo.setImageBitmap (photo.getCircleCroppedBitmap ()) : 당신은 이런 식으로 사용할 수
greenspand

여기서 photo는 함수로 확장 된 비트 맵 객체입니다
greenspand

1

자, 정답 :

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와 cy는 무엇입니까?
Kartik Chugh

1
K_7 @,이 원의 중심
마스터

1

코틴 기능

 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
        }

이 코드로 불러

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

1

가장 쉬운 해결책은 Bitmap의 BitmapShader를 만들고 페인트 객체에 전달한 다음 canvas.drawCircle (cx, cy, radius, paint);

예를 들면

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

이것이 https://github.com/hdodenhof/CircleImageView 가 수행 한 방법입니다 . 여기에서 소스 코드를 읽을 수 있습니다. 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

이것이 프로그래밍 질문인지 확실하지 않지만 ...

가장 쉬운 해결책은 소스 비트 맵에서 외부 영역을 투명하게 만드는 것입니다. 그렇지 않으면 원 밖에있는 픽셀을 계산하고 그에 따라 알파를 설정해야합니다 (완전한 투명도를 위해 알파 = 0).


솔직히 말해서, 나는 당신의 방식이었습니다. 작동하는 것처럼 보이지만 우리는 국경 재기 문제를 해결할 수 없습니다.
VinceStyling 2014 년

테두리 "굴곡"은 디더링 및 / 또는 앤티 앨리어싱으로 해결됩니다. 허용되는 것으로 보이는 것을 달성하기 위해 온라인에서 일부 알고리즘을 찾을 수 있습니다. 그러나 직사각형 픽셀과 곡선에는 항상 이러한 문제가 있음을 명심하십시오.
MandisaW
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.