6

使用一些数学,我创建了以下 java 函数来输入位图,并让它裁剪出一个居中的正方形,在该正方形中再次裁剪出一个圆圈,周围有一个黑色边框。正方形的其余部分应该是透明的。此外,通过 Messenger 发送图像时,两侧有透明距离,不会损坏预览。

我的函数代码如下:

 public static Bitmap edit_image(Bitmap src,boolean makeborder) {
        int width = src.getWidth();
        int height = src.getHeight();
        int A, R, G, B;
        int pixel;

        int middlex = width/2;
        int middley = height/2;

        int seitenlaenge,startx,starty;
        if(width>height) 
        {
            seitenlaenge=height;
            starty=0;

            startx = middlex - (seitenlaenge/2);
        }
        else 
        {
            seitenlaenge=width; 
            startx=0;

            starty = middley - (seitenlaenge/2);
        }

        int kreisradius = seitenlaenge/2;
        int mittx = startx + kreisradius;
        int mitty = starty + kreisradius;
        int border=2;
        int seitenabstand=55;

        Bitmap bmOut = Bitmap.createBitmap(seitenlaenge+seitenabstand, seitenlaenge+seitenabstand, Bitmap.Config.ARGB_8888);
        bmOut.setHasAlpha(true);

        for(int x = 0; x < width; ++x) {
            for(int y = 0; y < height; ++y) {
                int distzumitte = (int) (Math.pow(mittx-x,2) + Math.pow(mitty-y,2)); // (Xm-Xp)^2 + (Ym-Yp)^2 = dist^2
                distzumitte = (int) Math.sqrt(distzumitte);

                pixel = src.getPixel(x, y);

                A = Color.alpha(pixel);
                R = (int)Color.red(pixel);
                G = (int)Color.green(pixel);
                B = (int)Color.blue(pixel);
                int color = Color.argb(A, R, G, B);

                int afterx=x-startx+(seitenabstand/2);
                int aftery=y-starty+(seitenabstand/2);

                if(x < startx || y < starty || afterx>=seitenlaenge+seitenabstand || aftery>=seitenlaenge+seitenabstand) //seitenrand
                {
                    continue;
                }
                else if(distzumitte > kreisradius)
                {
                    color=0x00FFFFFF;
                }
                else if(distzumitte > kreisradius-border && makeborder) //border
                {
                    color = Color.argb(A, 0, 0, 0);
                }
                bmOut.setPixel(afterx, aftery, color);
            }
        }

        return bmOut;
    }

此功能工作正常,但出现了一些我无法解决的问题。

  • 图像质量显着下降
  • 边框不是真正的圆形,但在图像的边缘看起来是平的(在某些设备上?!)

对于这些问题,我将不胜感激。我不得不承认我在数学方面不是最好的,可能应该有一个更好的公式来划定边界。

4

3 回答 3

3

您的源代码很难阅读,因为它在变量名中混合了德语和英语。此外,您没有说您使用哪个图像库,因此我们不确切知道 Bitmap 和 Color 类的来源。

无论如何,很明显,您只在位图上进行操作。位图是指将整个图像逐个像素地存储在 RAM 中。没有有损压缩。我在您的源代码中看不到任何会影响图像质量的内容。

答案很可能在您没有向我们展示的代码中。此外,您所描述的(两个问题)听起来像是非常典型的低质量 JPEG 压缩。我敢肯定,在您调用函数后,您会将图像转换/保存为 JPEG。尝试在 BMP、TIFF 或 PNG 的那个位置执行此操作,然后看到错误神奇地消失了。也许您还可以在某处设置 JPEG 的质量级别以避免这种情况。

为了让其他人(也许)更容易找到一个好的答案,请允许我将您的代码翻译成英文:

    public static Bitmap edit_image(Bitmap src,boolean makeborder) {
        int width = src.getWidth();
        int height = src.getHeight();
        int A, R, G, B;
        int pixel;

        int middlex = width/2;
        int middley = height/2;

        int sideLength,startx,starty;
        if(width>height) 
        {
            sideLength=height;
            starty=0;

            startx = middlex - (sideLength/2);
        }
        else 
        {
            sideLength=width; 
            startx=0;

            starty = middley - (sideLength/2);
        }

        int circleRadius = sideLength/2;
        int middleX = startx + circleRadius;
        int middleY = starty + circleRadius;
        int border=2;
        int sideDistance=55;

        Bitmap bmOut = Bitmap.createBitmap(sideLength+sideDistance, sideLength+sideDistance, Bitmap.Config.ARGB_8888);
        bmOut.setHasAlpha(true);

        for(int x = 0; x < width; ++x) {
            for(int y = 0; y < height; ++y) {
                int distanceToMiddle = (int) (Math.pow(middleX-x,2) + Math.pow(middleY-y,2)); // (Xm-Xp)^2 + (Ym-Yp)^2 = dist^2
                distanceToMiddle = (int) Math.sqrt(distanceToMiddle);

                pixel = src.getPixel(x, y);

                A = Color.alpha(pixel);
                R = (int)Color.red(pixel);
                G = (int)Color.green(pixel);
                B = (int)Color.blue(pixel);
                int color = Color.argb(A, R, G, B);

                int afterx=x-startx+(sideDistance/2);
                int aftery=y-starty+(sideDistance/2);

                if(x < startx || y < starty || afterx>=sideLength+sideDistance || aftery>=sideLength+sideDistance) //margin
                {
                    continue;
                }
                else if(distanceToMiddle > circleRadius)
                {
                    color=0x00FFFFFF;
                }
                else if(distanceToMiddle > circleRadius-border && makeborder) //border
                {
                    color = Color.argb(A, 0, 0, 0);
                }
                bmOut.setPixel(afterx, aftery, color);
            }
        }

        return bmOut;
    }
于 2013-07-26T04:24:10.487 回答
1

关于质量,我看不出你的方法有什么问题。使用 Java Swing 运行代码不会丢失任何质量。唯一的问题是图像有锯齿边缘

随着屏幕分辨率的增加,锯齿问题将趋于消失,并且对于较低的分辨率会更加明显。这可能解释了为什么您只在某些设备中看到它。同样的问题适用于您的边框,但在这种情况下,它会更加明显,因为颜色是单一的黑色。

您的算法定义了原始图像的正方形区域。要找到正方形,它从图像的中心开始,然后扩展到图像的widthheight图像中较小的那个。我将这个区域称为square.

别名是由设置颜色的代码引起的(我使用的是伪代码):

if ( outOfSquare() ) {
    continue;  // case 1: this works but you depend upon the new image' s default pixel value i.e. transparent black
} else if ( insideSquare() && ! insideCircle() ) {
    color = 0x00FFFFFF;  // case 2: transparent white. <- Redundant
} else if ( insideBorder() ) {  
    color = Color.argb(A, 0, 0, 0);  // case 3: Black color using the transparency of the original image. 
} else { // inside the inner circle 
    // case 4: leave image color
}

关于代码的一些注释:

  • 情况 1 取决于原始图像的默认像素值,即透明黑色。它有效,但最好明确设置
  • 案例 2 是多余的。以与处理案例 1 相同的方式处理它。我们只对圈内发生的事情感兴趣。
  • 案例 3(当您绘制边界时)不清楚它的预期。如果原始 alpha 沿圆的边缘发生变化,则使用原始图像的 alpha 可能会弄乱您的新图像。所以这显然是错误的,并且取决于图像,可能是您问题的另一个原因。
  • 情况4没问题。

现在在您的圈子的外围发生以下颜色过渡:

  • 如果不使用边框:全透明->全图颜色(伪代码中的情况2和4)
  • 如果使用边框:全透明->全黑->全图像颜色(情况 2、3 和 4)

为了在边缘获得更好的质量,您需要引入一些中间状态,使过渡更平滑(新的过渡以斜体显示):

  • 不使用边框:全透明-> 部分透明带图像颜色 ->全图像颜色
  • 边框使用:全透明-> 部分透明黑色 ->全黑-> 部分透明黑色+图像颜色(即混合) ->全图像颜色

我希望这会有所帮助

于 2013-07-26T09:18:46.823 回答
1

我认为您需要检查 PorterDuffXferMode。

您将在此处找到有关合成图像模式的一些技术信息。

这里有一些制作圆角位图的好例子。你只需要调整一点源代码,你就可以开始了......

希望它会有所帮助。

于 2013-07-23T14:18:07.887 回答