【问题标题】:Android Bitmap save without transparent areaAndroid Bitmap保存没有透明区域
【发布时间】:2015-03-01 11:05:41
【问题描述】:

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

位图有大的透明像素。

所以我想删除它

我该怎么做?

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

我不想裁剪功能。 我希望使用过滤器

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

│透明区域

│ ┌────────┐

│裁剪这个
└────────┘
└────────────────────────┘

【问题讨论】:

标签: android bitmap crop


【解决方案1】:

要查找位图的不透明区域,请在 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);
}

【讨论】:

  • 精湛的工作,亲爱的!最近几天才发现这个!再次感谢您!
  • 这真的很棒。但执行需要一段时间。这是完成这项工作的最快方法吗?
  • @nitech 如果您在位图上调用 getPixels() 或 copyPixelsToBuffer(),那么您可以遍历像素数据,而无需为每个像素调用一次 getPixel()。这应该会加快速度,但会使事情复杂化,因为您必须考虑步幅并自己进行像素索引。在本机代码中执行此操作可能会更快,但似乎不必要地复杂。
  • 谢谢。我会在速度方面做更多的研究。
  • @nitech 更改算法也会加快速度。因为它做了很多不必要的检查。例如,您可以从顶部向下扫描,然后从底部向上扫描以找到最小和最大 y,然后在剩余区域中扫描每一行,但在每一行中只检查小于当前最小 x 和大于的区域当前的最大 x 来计算最小和最大 x。但是,乱序读取内存可能会影响性能。
【解决方案2】:

用这个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);
}

【讨论】:

  • 能不能在这里加一下sn-p的代码,这样其他人就不用去github repo了?
  • Done ;) @MartinGottweis。这段代码没有读取所有像素,只是切片垂直和水平矩阵,所以在我的测试中速度更快。
  • 在这一行崩溃 ...................... bitmap.getPixels(buffer, 0, 1, x, 顶部 + 1, 1, bufferSize); /////////////////// java.lang.IllegalArgumentException: y + height must be
【解决方案3】:

我采用了@Alvaro Menezes's answer 并将其改进为 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)
}

【讨论】:

    【解决方案4】:

    按照官方文档:

    新的位图可能与源是同一个对象,或者可能已经制作了一个副本。

    在使用源位图执行.recycle() 时应考虑到这一点。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-24
      • 2013-01-02
      • 2017-03-17
      • 1970-01-01
      • 2012-06-20
      • 2013-04-28
      • 1970-01-01
      • 2013-02-16
      相关资源
      最近更新 更多