【问题标题】:Properly load 24-bit bitmap data into a 32-bit Bitmap object将 24 位位图数据正确加载到 32 位位图对象中
【发布时间】:2015-04-20 12:33:23
【问题描述】:

我正在尝试将包含 24 位位图文件数据的字节数组加载到 Android 中的位图对象中,但我遇到了麻烦,因为位图仅支持 32 位和 16 位格式。这是我想出的将 24 位数据转换为 32 位的方法:

byte[] file = new byte[is.available()];
is.read(file);
is.close();
byte[] paddedFile = new byte[file.length + file.length / 3]; //we will be adding 1 more byte (alpha value) for each RGB triplet
for (int i = 0; i < file.length / 3; i++) {
    paddedFile[i * 4] = file[i * 3];
    paddedFile[i * 4 + 1] = file[i * 3 + 1];
    paddedFile[i * 4 + 2] = file[i * 3 + 2];
    paddedFile[i * 4 + 3] = -1; //manually added alpha channel
}
Bitmap screen = Bitmap.createBitmap(479, 616, Bitmap.Config.ARGB_8888); //exact dimensions of the 24-bit bitmap file
screen.copyPixelsFromBuffer(ByteBuffer.wrap(paddedFile));
iv.setImageBitmap(screen);

它有点/几乎可以工作。这是我正在使用的位图:

下面是上面代码之后的内容:

为什么会变形?非常感谢任何有关如何解决此问题的线索。

【问题讨论】:

  • 请显示图片
  • 我确实尝试先将 alpha 值添加到 paddedFile[i *4] 但我得到了奇怪的颜色。事实上,我分别尝试了 paddedFile[i *4] 和 paddedFile[i *4 + 3] 的值 -1/0/1,我在问题中发布的组合看起来最合理。
  • 似乎每个扫描线(文件的)都被填充了。如果文件是 BMP,则将其填充到最近的 4 字节(32 位)边界。尝试使用采用stride 参数的createBitmap 方法之一。

标签: java android bitmap


【解决方案1】:

如果我对填充的看法是正确的,您应该可以这样做:

int w = 479;
int h = 616;

byte[] file = /* as before */;

// Convert interleaved byte RGB to packed int ARGB
int[] paddedFile = new int[file.length / 3];
for (int i = 0; i < file.length / 3; i++) {
    paddedFile[i] = 0xff << 24 // Alpha (all opaque)
                  | ((file[i * 3] & 0xff) << 16) 
                  | ((file[i * 3 + 1] & 0xff) << 8) 
                  | ((file[i * 3 + 2] & 0xff)) 
}

int stride = w + (w % 4 == 0 ? 0 : 4 - (w % 4));
Bitmap screen = Bitmap.createBitmap(paddedFile, 0, stride, w, h, Bitmap.Config.ARGB_8888); 

【讨论】:

  • 与我 10 分钟前想出的相比,令人惊讶的是,您已经缩短了多少!基本上,在您最初发表评论后,我迷失在对步幅/位图填充的研究和计算中。为了完整起见,我也会发布我所拥有的。此外,使用 Bitmap screen = Bitmap.createBitmap(paddedFile, 0, stride, w, h, Bitmap.Config.ARGB_8888) 更短,不需要 API 级别 17 和额外的指标定义。很好的答案,谢谢。
【解决方案2】:

在 haraldK 发表评论后,我访问了Wikipedia page 关于位图格式,在那里我了解到位图字节数组基本上是行 * 高度。一行包含的字节数通过以下公式计算:

int paddedRow = (int) Math.floor((24 * w + 31) / 32) * 4; //1440 bytes

考虑到这是一个24位(3字节)的位图,每行的实际数据量为:

int actualRow = 3 * w; //1437 bytes

所以每行的填充量是:

int padding = paddedRow - actualRow; //3 bytes

这意味着每行 1440 的最后 3 个字节都是填充(其中有 616 个)。知道了这些值,我们就可以从字节数组中提取出真彩色数据,然后适当地追加 alpha 通道:

byte[] file = new byte[is.available()];
is.read(file);
is.close();
int w = 479;
int h = 616;
int paddedRow = (int) Math.floor((24 * w + 31) / 32) * 4;
int actualRow = 3 * w;
int padding = paddedRow - actualRow;
byte[] removedPadding = new byte[file.length - padding * h];

for (int j = 0, k = 0; j < h; j++) {
    int tmp = j * paddedRow;
    for (int i = 0 + tmp; i < actualRow + tmp; i++, k++) {
        removedPadding[k] = file[i];
    }
}

byte[] rgbaFile = new byte[removedPadding.length + removedPadding.length / 3];
for (int i = 0; i < removedPadding.length / 3; i++) {
    rgbaFile[i * 4] = removedPadding[i * 3];
    rgbaFile[i * 4 + 1] = removedPadding[i * 3 + 1];
    rgbaFile[i * 4 + 2] = removedPadding[i * 3 + 2];
    rgbaFile[i * 4 + 3] = -1;
}

Bitmap screen = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
screen.copyPixelsFromBuffer(ByteBuffer.wrap(rgbaFile));
iv.setImageBitmap(screen);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-28
    • 1970-01-01
    • 2020-04-08
    相关资源
    最近更新 更多