扭曲的位图图像
// load pixels into an image
this.image = new BufferedImage(this.width,
this.height,
BufferedImage.TYPE_INT_RGB);
// get actual image data for easier pixel loading
byte[] iData = new byte[this.size - 54];
for(int i = 0; i < this.size - 54; i++) {
iData[i] = this.data[i+54];
}
// start from bottom row
for(int y = this.height-1; y >= 0; y--) {
for(int x = 0; x < this.width; x++) {
int index = (this.width*y + x) * 3;
int b = iData[index];
int g = iData[index+1];
int r = iData[index+2];
//System.out.format("R: %s\nG: %s\nB: %s\n\n", r, g, b);
// merge rgb values to single int
int rgb = ((r&0x0ff)<<16)|((g&0x0ff)<<8)|(b&0x0ff);
// build image from bottom up
this.image.setRGB(x, this.height-1-y, rgb);
}
}
我正在读取位图中的RGB值。我的iData字节数组是正确的,因为我使用十六进制编辑器对其进行了检查。但是,当我运行这个循环时,我的输出图像被扭曲(见图片)。为了解决这个问题,我一直在困扰我的大脑几个小时,为什么会这样呢?扭曲的位图图像
输入图像是加拿大国旗。
输出图像:
我不占上的宽度的零字节填充,其中式是
WidthWithPadding =天花板((W *颜色质量汇总)/ 32)* 32。
图像通常具有宽度,深度(每像素位数)和步幅。通常,步幅不仅仅是宽度*深度,而是填充了一定量(通常用于将每行对齐到16位边界以用于内存访问原因)。
您的代码看起来并没有考虑步幅或任何填充,这将解释图像中的偏移量。我很惊讶颜色没有被切换(暗示填充与像素大小相同),但是那些未定义的值导致图像中的黑色条纹。
为了得到您的索引,您应该使用(this.stride*y + x) * 3
,而不是乘以(this.width*y + x) * 3
以获得适合的步幅值。 BufferedImage class似乎没有提供任何明显的方式,所以你需要计算或获取否则。
发生的一般问题是,您的代码将宽度(行中的字节数)与stride(以字节为单位的行之间的距离)混合在一起;在对齐的数据格式中,这些不一定是相同的东西。这样做的一般结果就是出现了你所看到的歪斜,因为第二行从第一行的最后一个字节开始,第三行从倒数第二个字节开始,第二排,等等。
您需要做的第一件事是计算数据的步幅,并将其用作该行的乘法因子。下面是一个简化的循环,也就是一定程度上比在每个像素的基础上相乘更有效:
int stride = (this.width + 3) & ~3; // rounds up to the nearest multiple of 4
for (int y = 0; y < this.height; y++) {
int idx = y*stride;
for (int x = 0; x < this.width; x++) {
int b = iData[idx++];
int g = iData[idx++];
int a = iData[idx++];
// etc.
}
}
注意,舍入特技上述((w + a - 1) & ~(a - 1)
)仅适用于幂的两个值;舍入招的更一般的形式是:
int stride = (width + stride - 1)/stride*stride;
虽然这是非常罕见的发现,是不是2摆在首位的功率比对。
您的输入文件是否有4字节对齐的行?有些格式将填充添加到每行的末尾,以使每行长度为4个字节的倍数。 – immibis 2014-10-09 04:25:58
是的,它的确如此!为什么? – Tetramputechture 2014-10-09 04:29:22
因为你可能没有考虑到这一点。 – immibis 2014-10-09 04:30:09