Android位图保存时去除透明区域

14

我想保存没有透明区域的位图。

位图有大量透明像素。

所以我想去掉它们。

我该怎么做?

我不能添加图片,所以请用符号解释。

我不想使用裁剪功能。

我希望使用过滤器。

┌────────────────────────┐

│ 透明区域

│ ┌────────┐

│ 剪辑这部分
└────────┘
└────────────────────────┘


你想用颜色填充透明区域吗? - balaji koduri
不,我想使用滤镜裁剪位图而不包括透明区域。 - user1066874
尝试这个答案:https://dev59.com/kG025IYBdhLWcg3w6KSO#23774150 - balaji koduri
4个回答

28

要找到位图的不透明区域,请在位图的x和y上进行迭代,并找到非透明区域的最小值和最大值。然后将位图裁剪到这些坐标。

Bitmap CropBitmapTransparency(Bitmap sourceBitmap)
{
    int minX = sourceBitmap.getWidth();
    int minY = sourceBitmap.getHeight();
    int maxX = -1;
    int maxY = -1;
    for(int y = 0; y < sourceBitmap.getHeight(); y++)
    {
        for(int x = 0; x < sourceBitmap.getWidth(); x++)
        {
            int alpha = (sourceBitmap.getPixel(x, y) >> 24) & 255;
            if(alpha > 0)   // pixel is not 100% transparent
            {
                if(x < minX)
                    minX = x;
                if(x > maxX)
                    maxX = x;
                if(y < minY)
                    minY = y;
                if(y > maxY)
                    maxY = y;
            }
        }
    }
    if((maxX < minX) || (maxY < minY))
        return null; // Bitmap is entirely transparent

    // crop bitmap to non-transparent area and return:
    return Bitmap.createBitmap(sourceBitmap, minX, minY, (maxX - minX) + 1, (maxY - minY) + 1);
}

太棒了,亲爱的!最近几天才发现这个!再次感谢你! - Mayur R. Amipara
这个方法确实很好用。但是执行起来需要一些时间。这是做这项工作最快的方式吗? - nitech
如果您在位图上调用getPixels()或copyPixelsToBuffer(),则可以迭代像素数据而无需为每个像素调用getPixel()。这应该会加快速度,但会使事情变得复杂,因为您必须考虑步幅并自己进行像素索引。在本地代码中执行此操作可能仍然更快,但似乎过于复杂。 - samgak
谢谢。我会再做一些关于速度的研究。 - nitech
@nitech 更改算法也会加快速度。目前的算法进行了许多不必要的检查。例如,你可以从顶部向下扫描,然后从底部向上扫描以找到最小和最大的y值,然后在剩余区域中扫描每一行, 但是在每一行中只检查当前最小x和当前最大x之间小于当前区域并大于当前区域的最小和最大x值。但是,读取内存顺序可能会影响性能。 - samgak
显示剩余3条评论

8
我采用了@Alvaro Menezes的答案并将其改进为Kotlin扩展函数。我对其进行了一些微调,更改了一些变量名称以提高可读性,并添加了更多修复措施,以解决由@Ahamadullah Saikat提出的抛出IllegalArgumentException的问题。
请注意,按行读取像素比独立读取像素能够显著提高性能,这与已接受的答案建议的方法不同。
/**
 * Trims a bitmap borders of a given color.
 *
 */
fun Bitmap.trim(@ColorInt color: Int = Color.TRANSPARENT): Bitmap {

    var top = height
    var bottom = 0
    var right = width
    var left = 0

    var colored = IntArray(width, { color })
    var buffer = IntArray(width)

    for (y in bottom until top) {
        getPixels(buffer, 0, width, 0, y, width, 1)
        if (!Arrays.equals(colored, buffer)) {
            bottom = y
            break
        }
    }

    for (y in top - 1 downTo bottom) {
        getPixels(buffer, 0, width, 0, y, width, 1)
        if (!Arrays.equals(colored, buffer)) {
            top = y
            break
        }
    }

    val heightRemaining = top - bottom
    colored = IntArray(heightRemaining, { color })
    buffer = IntArray(heightRemaining)

    for (x in left until right) {
        getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
        if (!Arrays.equals(colored, buffer)) {
            left = x
            break
        }
    }

    for (x in right - 1 downTo left) {
        getPixels(buffer, 0, 1, x, bottom, 1, heightRemaining)
        if (!Arrays.equals(colored, buffer)) {
            right = x
            break
        }
    }
    return Bitmap.createBitmap(this, left, bottom, right - left, top - bottom)
}

8
使用此Github,可以裁剪带透明边框的图像。
public static Bitmap crop(Bitmap bitmap) {

    int height = bitmap.getHeight();
    int width = bitmap.getWidth();
    int[] empty = new int[width];
    int[] buffer = new int[width];
    Arrays.fill(empty, 0);
    int top = 0;
    int left = 0;
    int bottom = height;
    int right = width;

    for (int y = 0; y < height; y++) {
        bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
        if (!Arrays.equals(empty, buffer)) {
            top = y;
            break;
        }
    }

    for (int y = height - 1; y > top; y--) {
        bitmap.getPixels(buffer, 0, width, 0, y, width, 1);
        if (!Arrays.equals(empty, buffer)) {
            bottom = y;
            break;
        }
    }

    empty = new int[height];
    buffer = new int[height];
    Arrays.fill(empty, 0);

    for (int x = 0; x < width; x++) {
        bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
        if (!Arrays.equals(empty, buffer)) {
            left = x;
            break;
        }
    }

    for (int x = width - 1; x > left; x--) {
        bitmap.getPixels(buffer, 0, 1, x, 0, 1, height);
        if (!Arrays.equals(empty, buffer)) {
            right = x;
            break;
        }
    }

    return Bitmap.createBitmap(bitmap, left, top, right - left + 1, bottom - top + 1);
}

你能否在这里添加代码片段,这样其他人就不必去github仓库了吗? - Martin Gottweis
这段代码并没有读取所有像素,只是切割了垂直和水平矩阵,所以在我的测试中速度更快。 - Álvaro Menezes
4
在这一行崩溃了 ................................ bitmap.getPixels(buffer, 0, 1, x, top + 1, 1, bufferSize); /////////////////////// java.lang.IllegalArgumentException: y + height 必须小于等于 bitmap.height() - Ahamadullah Saikat

0

根据官方文档:

新位图可以是与源相同的对象,也可以是已复制的副本。

在执行 .recycle() 时,您应该考虑源位图。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接