BMP 宽度/高度尺寸超过 255?
Posted
技术标签:
【中文标题】BMP 宽度/高度尺寸超过 255?【英文标题】:BMP Width/Height size over 255? 【发布时间】:2014-05-12 10:44:06 【问题描述】:众所周知,每个字节都是无符号字符,这意味着范围为 0 - 255。 我写了一个可以编写 BMP 的代码,该代码可以工作。问题是当我尝试编写一个宽度/高度大于 255 的 bmp 时,它显示为小于 255 的值。
在宽度的下一点上,我刚刚输入了1
,它可以工作。它用 (500) 输出实数,但我希望它也适用于高度.. 但事实并非如此。图像坏了。
更新:高度超过 214 会导致 bmp 损坏。我想念什么? 这是我的代码:
typedef unsigned char BYTE; //1
typedef unsigned short WORD; //2
typedef unsigned long DWORD; //4
typedef struct BMP_HEADER
BYTE bmp_type[2]; //2
BYTE bmp_size[4]; //4
BYTE bmp_as[4]; //4
BYTE bmp_offset[4]; //4
BMP;
typedef struct DIB_HEADER
BYTE dib_size[4]; //4
DWORD dib_BMPwidth; //4
DWORD dib_BMPheight; //4
BYTE dib_BMPcplanes[2]; //2
BYTE dib_BMPBPX[2]; //2
BYTE dib_BIRGB[4]; //4
BYTE dib_rawsize[4]; //4
BYTE dib_Xresolution[4]; //4
BYTE dib_Yresolution[4]; //4
BYTE dib_cpalette[4]; //4
BYTE dib_cimportant[4]; //4
DIB;
typedef struct PIXEL_ARRAY
BYTE BGR[3]; //3
PIX;
BYTE* PAD;
DWORD padding(DWORD BMPwidth)
int pitch = BMPwidth * 3;
if (pitch % 4 != 0)
pitch += 4 - (pitch % 4);
return pitch - (BMPwidth * 3);
void create_bmp(char BMPname[], DWORD BMPwidth, DWORD BMPheight, WORD BMPR, WORD BMPG, WORD BMPB)
// variables
char build_name[256];
FILE* fp;
BMP newBMP;
DIB newDIB;
PIX* template;
gedMaxGameMem = 2827465479;
if(BMPwidth > 1 && BMPheight > 1) template = (PIX*)malloc(BMPwidth*BMPheight);
else template = (PIX*)malloc(sizeof(PIX*)*max(BMPwidth, BMPheight));
// make file
sprintf(build_name, "%s.bmp", BMPname);
fp = fopen(build_name, "wb");
// BM
newBMP.bmp_type[0] = 'B';
newBMP.bmp_type[1] = 'M';
fwrite(&newBMP.bmp_type, 1, 2, fp);
// SIZE
DWORD sz = (sizeof(newBMP) + sizeof(newDIB) + sizeof(template));
fwrite(&sz, 1, 1, fp);
// Application specifics
fwrite(&newBMP.bmp_as, 1, 4, fp);
// offset
newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
fwrite(&newBMP.bmp_offset, 1, 4, fp);
//
// DIB SIZE
newDIB.dib_size[0] = sizeof(newDIB);
fwrite(&newDIB.dib_size, 1, 4, fp);
// DIB Image width
newDIB.dib_BMPwidth = BMPwidth;
fwrite(&newDIB.dib_BMPwidth, 1, 4, fp);
// DIB Image height
newDIB.dib_BMPheight = BMPheight;
fwrite(&newDIB.dib_BMPheight, 1, 4, fp);
// DIB Color planes
newDIB.dib_BMPcplanes[0] = 1;
fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);
// DIB BMP Bits per pixel
newDIB.dib_BMPBPX[0] = 24;
fwrite(&newDIB.dib_BMPBPX, 1, 2, fp);
// DIB Compression method
fwrite(&newDIB.dib_BIRGB, 1, 4, fp);
// DIB Raw size
newDIB.dib_rawsize[0] = sizeof(template);
fwrite(&newDIB.dib_rawsize, 1, 4, fp);
// DIB Xresolution
newDIB.dib_Xresolution[0] = 19;
newDIB.dib_Xresolution[1] = 11;
fwrite(&newDIB.dib_Xresolution, 1, 4, fp);
// DIB Yresolution
newDIB.dib_Yresolution[0] = 19;
newDIB.dib_Yresolution[1] = 11;
fwrite(&newDIB.dib_Yresolution, 1, 4, fp);
// DIB Colors palette
fwrite(&newDIB.dib_cpalette, 1, 4, fp);
// DIB Important colors
fwrite(&newDIB.dib_cimportant, 1, 4, fp);
//
int i;
for(i=0; i<BMPwidth+1; i++)
template[i].BGR[0] = BMPB;
template[i].BGR[1] = BMPG;
template[i].BGR[2] = BMPR;
fwrite(template->BGR, 3, i, fp);
free(template);
fclose(fp);
【问题讨论】:
您似乎正在写入许多未初始化的值。例如,我看不到您将 newDIB 结构归零。您只需设置一些字段,例如 dib_BMPheight[0](有时还有 dib_BMPheight[1]),但不设置 dib_BMPheight[2] 和 dib_BMPheight[3]。然后你把它们写出来,所以它们可能是垃圾。 还要注意步幅可能与宽度不匹配。 (步幅是从一个扫描线到下一个扫描线的字节数。)每个扫描线必须与一个四字节边界对齐。那就是步幅必须是 4 的倍数。由于每个像素写入 3 个字节,因此只有当宽度是 4 的倍数时,步幅才匹配宽度*3。 “未初始化”值是什么意思?为什么我必须将 newDIB 结构“归零”,我使用 fwrite 来写入我想要的字节。而且宽度现在也有效,高度现在给我带来了问题。如果步幅日志。东西可以帮助我..我怎样才能用它来指定高度的步幅?newDIB.dib_size[0] = sizeof(newDIB); fwrite(&newDIB.dib_size, 1, 4, fp);
将四个字节写入文件,但您只设置了其中一个。其他三个未初始化。当您在堆栈上实例化一个变量时,它的值可以是任何值,直到您将其显式设置为某个值。如果变量是一个结构,那么你必须在使用它们之前初始化所有其中的字段。
我忘记了,谢谢 :) 即便如此.. 我在审查 BMP 结构的 1 小时内成功地做了一些事情,而且我的英语甚至很糟糕。现在我认为我对更改的初始化程序和使用的填充没有问题。
【参考方案1】:
如果你想把宽度和高度作为字节来摆弄,你不能只将第二个字节设置为1,你必须计算出确切的值,例如:
uint32_t h = 1024;
height[0] = h & 0xff;
height[1] = (h >> 8) & 0xff;
height[2] = (h >> 16) & 0xff;
height[3] = (h >> 24) & 0xff;
(这里用& 0xff
掩码真的是不必要的,反正在将数字拟合成一个字节时已经完成了。)
但也许你应该首先在你的结构中使用更大的整数类型:
typedef struct DIB_HEADER
DWORD dib_size;
DWORD dib_BMPwidth;
DWORD dib_BMPheight;
WORD dib_BMPcplanes];
...
;
(这可能会导致字节序问题,但我认为 Windows 位图以 Little-Endian 格式存储,因此这里不应该成为问题。)
编辑:进一步查看您的代码,我发现一些错误:
如果要为像素分配临时内存,则必须为每个像素分配 3 个字节,即sizeof(PIX) * BMPwidth + BMWheight
。您对单行、单列和其他位图的区分毫无意义,并且还引入了错误(即没有为每个像素分配每个像素的大小。)
您正在编写仅一种颜色的单色位图,因此您实际上不需要创建巨大的临时位图;您可以一遍又一遍地写入相同的像素。
您在堆栈上创建BMP
和DIB
结构;因此,它们可能未初始化,并且在您未明确初始化的字段中有垃圾。
您不必单独编写每个字段,而是可以编写整个结构。 (但是您必须去掉"BM"
标记,因为结构将在那里填充以使下一个字段从四字节边界开始。您还可以使用编译器选项来始终紧紧地打包结构。 )
当您写出数据时,您只循环宽度,但您必须在嵌套循环中同时循环宽度和高度。
当你用fwrite
写出数据时,你的论点是错误的:你可能想写fwrite(&template[i]->RGB, 3, 1, fp)
。但是您的项目计数是i
,这意味着您一遍又一遍地从头开始编写所有内容,但始终附加最后一个字节,例如“AABAABCABCDABCDE”。 (当所有像素都具有相同的颜色时,这无关紧要,但您的文件大小将会关闭。)
BMP 行数据填充为 4 个字节。您为此编写了一个例程,但不要调用它。
换句话说。您的代码没有为超过一行的数据做好准备。 :-)
这是编写单色位图的代码的更简单版本:
void create_bmp_uni(const char *fn, int w, int h, int r, int g, int b)
FILE* fp;
DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
DWORD pad = rsize - w * sizeof(PIX);
DWORD rawsize = rsize * h * sizeof(PIX);
BYTE zero[3] = 0;
const char *id = "BM";
BMP bmp =
2 + sizeof(BMP) + sizeof(DIB) + rawsize,
0,
2 + sizeof(BMP) + sizeof(DIB)
;
DIB dib =
sizeof(DIB),
w,
h,
1,
24,
0,
rawsize,
2835,
2835,
0,
0
;
PIX pix = b, g, r;
int i, j;
fp = fopen(fn, "wb");
fwrite(id, 1, 2, fp);
fwrite(&bmp, 1, sizeof(bmp), fp);
fwrite(&dib, 1, sizeof(dib), fp);
for(i = 0; i < h; i++)
for(j = 0; j < w; j++)
fwrite(&pix, sizeof(pix), 1, fp);
if (pad) fwrite(zero, 1, pad, fp);
fclose(fp);
(.bmp
后缀必须由调用者添加。还要注意 BM 标记是如何与 BMP
结构的其余部分分开编写的。)
【讨论】:
顺便提一下,我使用的是非常古老的编译器,我不知道它是否甚至在 C89 下并且不包括 uint32。 从 BYTE 更改为 DWORD 仅适用于宽度。如果我用高度这样做,它就不会绘制图像。 固定长度的整数类型在<stdint.h>
中定义。但由于您使用的是 Windows,因此您可以使用 DWORD
代替 'uint32_t` 和 WORD
代替 uint16_t
。
是的,我明白了:)。但是身高还是不想出来。将高度弄乱超过214
会导致 bmp 损坏。
但这与结构字段中不正确的高度无关。您的内存分配以及写出数据的方式不正确。我会更新我的答案【参考方案2】:
输出: http://i.stack.imgur.com/fqxO9.png(每次都有不同的颜色) 当前代码:
typedef unsigned char BYTE; //1
typedef unsigned short WORD; //2
typedef unsigned long DWORD; //4
typedef struct BMP_HEADER
BYTE bmp_size[4]; //4
BYTE bmp_as[4]; //4
BYTE bmp_offset[4]; //4
BMP;
typedef struct DIB_HEADER
BYTE dib_size[4]; //4
DWORD dib_w; //4
BYTE dib_h[4]; //4
BYTE dib_BMPcplanes[2]; //2
BYTE dib_BMPBpix[2]; //2
BYTE dib_BIRGB[4]; //4
BYTE dib_rawsize[4]; //4
BYTE dib_Xresolution[4]; //4
BYTE dib_Yresolution[4]; //4
BYTE dib_cpalette[4]; //4
BYTE dib_cimportant[4]; //4
DIB;
//_______________________________________________________________________________________________//
typedef struct PIXEL_ARRAY
BYTE BGR[3]; //3
PIX;
BYTE* PAD;
DWORD padding(DWORD w)
int pitch = w * 3;
if (pitch % 4 != 0)
pitch += 4 - (pitch % 4);
return pitch - (w * 3);
DWORD row_sz(WORD BMPBpix, DWORD w)
return ((BMPBpix*w+31)/32)*4;
void create_bmp(char BMPname[], DWORD w, DWORD h, BYTE R, BYTE G, BYTE B)
// variables
char build_name[256];
FILE* fp;
BMP newBMP;
DIB newDIB;
PIX* pix;
DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
DWORD pad = rsize - w * sizeof(PIX);
DWORD rawsize = rsize * h * sizeof(PIX);
BYTE zero[3] = 0;
const char *id = "BM";
int i, j;
gedMaxGameMem = 2827465479;
if(w > 1 && h > 1) pix = (PIX*)malloc(w*h);
else pix = (PIX*)malloc(sizeof(PIX*)*max(w, h));
// make file
sprintf(build_name, "%s.bmp", BMPname);
fp = fopen(build_name, "wb");
// BM
fwrite(id, 1, 2, fp);
// SIZE
newBMP.bmp_size[0] = (sizeof(newBMP) + sizeof(newDIB) + sizeof(pix));
fwrite(&newBMP.bmp_size, 1, 4, fp);
// Application specifics
fwrite(&newBMP.bmp_as, 1, 4, fp);
// offset
newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
fwrite(&newBMP.bmp_offset, 1, 4, fp);
//
// DIB SIZE
newDIB.dib_size[0] = sizeof(newDIB);
fwrite(&newDIB.dib_size, 1, 4, fp);
// DIB Image width
newDIB.dib_w = w;
fwrite(&newDIB.dib_w, 1, 4, fp);
// DIB Image height
newDIB.dib_h[0] = h;
fwrite(&newDIB.dib_h, 1, 4, fp);
// DIB Color planes
newDIB.dib_BMPcplanes[0] = 1;
fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);
// DIB BMP Bits per pixel
newDIB.dib_BMPBpix[0] = 24;
fwrite(&newDIB.dib_BMPBpix, 1, 2, fp);
// DIB Compression method
fwrite(&newDIB.dib_BIRGB, 1, 4, fp);
// DIB Raw size
newDIB.dib_rawsize[0] = sizeof(pix);
fwrite(&newDIB.dib_rawsize, 1, 4, fp);
// DIB Xresolution
newDIB.dib_Xresolution[0] = 19;
newDIB.dib_Xresolution[1] = 11;
fwrite(&newDIB.dib_Xresolution, 1, 4, fp);
// DIB Yresolution
newDIB.dib_Yresolution[0] = 19;
newDIB.dib_Yresolution[1] = 11;
fwrite(&newDIB.dib_Yresolution, 1, 4, fp);
// DIB Colors palette
fwrite(&newDIB.dib_cpalette, 1, 4, fp);
// DIB Important colors
fwrite(&newDIB.dib_cimportant, 1, 4, fp);
//
for(i = 0; i < h; i++)
for(j = 0; j < w; j++)
fwrite(&pix, sizeof(pix), 1, fp);
if (pad) fwrite(zero, 1, pad, fp);
fclose(fp);
【讨论】:
以上是关于BMP 宽度/高度尺寸超过 255?的主要内容,如果未能解决你的问题,请参考以下文章
我尝试用 c++ 编写一个 bmp 文件,但是 bmp 文件的宽度和高度似乎会影响结果,有时我得到了一个错误的 bmp 文件