【问题标题】:How to crop circular area from bitmap in Android如何在Android中从位图中裁剪圆形区域
【发布时间】:2012-08-09 14:15:13
【问题描述】:

我有一个位图,我想从这个位图中裁剪一个圆形区域。圆圈外的所有像素都应该是透明的。我该怎么做?

【问题讨论】:

    标签: android android-layout user-interface graphics


    【解决方案1】:

    经过长时间的头脑风暴,我找到了解决方案

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

    【讨论】:

    • 您也可以通过根据圆形剪切路径剪切位图来做到这一点。您可以在每次绘制位图时执行此操作,这意味着您实际上永远不会创建具有透明像素的位图,或者您可以将剪裁的位图绘制到事先已擦除为透明的缓冲区中。我认为两者都会比这更快更简单。
    • 谢谢。您的代码效果惊人。现在我还可以使用路径(多边形)进行裁剪。
    • 这个方法可以被制作成static 并用于类似静态方法的非实例化实用程序类中。
    • 您不应该在这里使用最小高度和宽度除以 2 作为半径吗? canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint);
    • 有3个关键点:1)创建一个空位图并画一个圆。 2) 将 xfermode 设置为 SRC_IN。 3) 将真实位图绘制到画布边界。 所以,油漆颜色和其他画布绘制没有用。
    【解决方案2】:

    从矩形生成圆形

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

    【讨论】:

    • 我建议在一个框架布局中使用两个图像视图,顶部的图像视图带有透明圆圈。
    【解决方案3】:

    您可以使用 RoundedBitmapDrawable 使您的 imageview 成为圆形

    这里是实现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 支持库选项。否则我想我不会知道的。
    • 并使用 setCircular(true) 而不是 setCornerRadius(50.0f) 使可绘制对象成为圆形。注意:图片必须是正方形或者纵横比有缺陷...
    【解决方案4】:

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

    这可以添加到实用程序类中。

    【讨论】:

    • 我正要发布非常相似的代码。问题是根据developer.android.com/guide/topics/graphics/hardware-accel.html,硬件加速不支持clipPath。我实际上在一个应用程序中遇到了这个问题,并想知道发生了什么。然而,较新的硬件似乎可以解决这个问题(如谷歌平板电脑)。一种可能的对代码的进一步清理:绘制位图时不需要进行 rect 到 rect 的转换。你可以说c.drawBitmap(b, 0, 0, null);,它使用默认的身份转换。
    • 您是如何在使用硬件加速的同时使用 clipPath 的?
    • 我之前最初使用此解决方案,但输出有锯齿状边缘。 @Altaf 的解决方案效果更好
    • 非常适合裁剪状态栏通知中使用的图像
    【解决方案5】:

    我认为此解决方案适用于任何类型的矩形,如果您希望图像变小或变大,请更改像素大小:

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

    【讨论】:

      【解决方案6】:

      这也可以在 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>
      

      输出图像视图:

      希望,它可能对某人有用!!! :)

      【讨论】:

      • 似乎只有当图像具有小于圆形的透明背景时它才有效......
      • 你只是把一个 ImageView 放在另一个上面,这不是面具 :)
      • @Ash 当然,你是对的 :) 我只是这样你就不会真正“裁剪”原始图像,正如原始海报所要求的那样;)
      【解决方案7】:

      你可以使用这个代码,它会工作

       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
      • 它正在创建椭圆位图而不是圆形。
      【解决方案8】:

      你可以使用这个代码,它会工作

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

      【讨论】:

        【解决方案9】:

        我相信最简单的解决方案是为您的位图创建一个 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

        【讨论】:

          【解决方案10】:

          如果您不再需要它,我建议添加bitmap.recycle(),它将防止出现 OutOfMemory 错误。

          【讨论】:

            【解决方案11】:

            这是使用扩展方法的 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
                }
            }
            

            【讨论】:

              【解决方案12】:

              对于想要矩形中心的人(我),在切割之前添加:

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

              【讨论】:

                【解决方案13】:

                根据 [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
                }
                

                【讨论】:

                • 你可以把它转换成一个扩展函数
                • 类似有趣的 Bitmap.getCircleCroppedBitmap(): Bitmap {} 并使用它而不是 originalBitmap
                • 那么你可以像这样使用它:img_user_photo.setImageBitmap(photo.getCircleCroppedBitmap())
                • 其中photo是用函数扩展的位图对象
                【解决方案14】:

                现在,正确答案:

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

                【讨论】:

                • 什么是 cx 和 cy?
                • @K_7,这是圆心
                【解决方案15】:

                Kotin 函数

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

                【讨论】:

                  【解决方案16】:
                  **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);
                      }
                  

                  【讨论】:

                    【解决方案17】:

                    对于 kotlin

                    private fun getCircularBitmap(bitmap: Bitmap): 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)
                            paint.isAntiAlias = true
                            canvas.drawARGB(0, 0, 0, 0)
                            paint.color = color
                            canvas.drawCircle(
                                (bitmap.width / 2).toFloat(), (bitmap.height / 2).toFloat(), (
                                        bitmap.width / 2).toFloat(), paint
                            )
                            paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
                            canvas.drawBitmap(bitmap, rect, rect, paint)
                            return output
                        }
                    

                    【讨论】:

                      【解决方案18】:

                      kotlin,放到Ext.kt中

                       private fun Bitmap.getCircledBitmap(): Bitmap {
                          val output = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
                          val canvas = Canvas(output)
                          val paint = Paint()
                          val rect = Rect(0, 0, this.width, this.height)
                          paint.isAntiAlias = true
                          canvas.drawARGB(0, 0, 0, 0)
                          canvas.drawCircle(this.width / 2f, this.height / 2f, this.width / 2f, paint)
                          paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
                          canvas.drawBitmap(this, rect, rect, paint)
                          return output
                      }
                      

                      【讨论】:

                        【解决方案19】:

                        不确定这是一个编程问题,但是...

                        最简单的解决方案是使源位图中的外部区域透明。否则,您必须计算哪些像素在圆外,并相应地设置 alpha(alpha = 0 表示完全透明)。

                        【讨论】:

                        • 说实话,我一直是你的方式,看起来可行但我们无法解决边界锯齿问题,不是吗?
                        • 边界“锯齿”通过抖动和/或抗锯齿来解决。你可以在网上寻找一些算法来完成一些看起来可以接受的事情。但请记住,矩形像素和曲线总会有这些问题。
                        猜你喜欢
                        • 2020-01-31
                        • 1970-01-01
                        • 2017-07-05
                        • 1970-01-01
                        • 1970-01-01
                        • 2012-10-08
                        • 2020-04-13
                        • 2020-09-30
                        • 1970-01-01
                        相关资源
                        最近更新 更多