我正在用C做一个位图编写器,这是一个较大项目的一部分。我遵循了Windows .bmp标头格式规范,并在十六进制编辑器中检查了生成的文件,以将其与功能性.bmp图像进行比较,但是我计算机上的所有图像程序都无法打开它。这是代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#pragma pack(push, 1) /* remove padding from sructs */
void generateBitmap(int width, int height, float dpi, const char* filename, pixel* imgData) {
FILE* bitmap;
struct fHeader {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
}bmpFileHeader;
struct iHeader {
uint32_t headerSize;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bitCount;
uint32_t compression;
uint32_t imageSize; /* may be 0 if uncompressed */
int32_t xPPM;
int32_t yPPM;
uint32_t colorEntriesUsed;
uint32_t importantColors;
}bmpImageHeader;
int bytesPerPixel = 3; /* 24 bit color */
uint32_t imgSize = width * height;
uint32_t fileSize = sizeof(bmpFileHeader) + sizeof(bmpImageHeader) + (bytesPerPixel * width * height);
int32_t ppm = dpi * 39;
bmpFileHeader.type = 0x4D42;
bmpFileHeader.size = fileSize;
bmpFileHeader.reserved1 = 0;
bmpFileHeader.reserved2 = 0;
bmpFileHeader.offset = sizeof(bmpFileHeader) + sizeof(bmpImageHeader);
bmpImageHeader.headerSize = sizeof(bmpImageHeader);
bmpImageHeader.width = width;
bmpImageHeader.height = height;
bmpImageHeader.planes = 1;
bmpImageHeader.bitCount = 8 * bytesPerPixel;
bmpImageHeader.compression = 0;
bmpImageHeader.imageSize = bytesPerPixel * height * width;
bmpImageHeader.xPPM = ppm; /* typically set these to zero */
bmpImageHeader.yPPM = ppm;
bmpImageHeader.colorEntriesUsed = 0;
bmpImageHeader.importantColors = 0;
bitmap = fopen(filename, "wb");
fwrite(&bmpFileHeader, 1, sizeof(bmpFileHeader), bitmap);
fwrite(&bmpImageHeader, 1, sizeof(bmpImageHeader), bitmap);
int i;
for (i = 0; i < (width * height); i++) {
fwrite(&imgData[i], 3, sizeof(char), bitmap);
}
fclose(bitmap);
}
int main(void) {
pixel imData[4];
int i;
for(i = 0; i < 4; i++) {
imData[i].r = 32;
imData[i].g = 64;
imData[i].b = 32;
}
generateBitmap(2, 2, 0, "bmptest.bmp", imData);
return 0;
}
最终结果应仅为2x2单调图像。我发现一些示例将更多的标头值(例如imageSize
)设置为零,但其他示例将其视为此处的示例。
以位图格式填充像素,以便每一行的“字节宽度”为4字节的倍数。有关更多详细信息,请参见link。
在这种情况下,每行有2个像素,每个像素为3个字节。因此,每行有6个字节。它必须被填充,所以每行是8个字节(最后2个字节将被忽略)。
这也会影响位图文件的大小以及imageSize
结构的iHeader
。
int bytesPerPixel = 3;
int bits_per_pixel = bytesPerPixel * 8;
int width_in_bytes = ((width * bits_per_pixel + 31) / 32) * 4;
...
bmpFileHeader.size = fileSize;
...
uint32_t fileSize = sizeof(bmpFileHeader) + sizeof(bmpImageHeader) + width_in_bytes * height;
...
bmpImageHeader.imageSize = width_in_bytes * height;
您要重写循环,使其从上到下,从左到右
for(int y = height - 1; y >= 0; y--)
{
for(int x = 0; x < width; x++)
{
int i = y * width + x;
fwrite(&imgData[i], 3, sizeof(char), bitmap);
}
for(int x = width * bits_per_pixel / 8; x < width_in_bytes; x++)
fputc('\0', bitmap);
}
最近您可以使用#pragma pack(pop)
恢复编译器的默认包
#pragma pack(push, 1) /* remove padding from sructs */
struct fHeader {
...
};
struct iHeader {
...
};
#pragma pack(pop)
好吧,我终于可以使用它了。问题出在像素行之间的偏移填充位。我个人没有发现它的文档记录得非常好,但是.bmp
文件格式期望每行的末尾都有空的填充字节,具体取决于图像的尺寸。我要做的就是将这一行添加到像素编写代码中:
int i;
int p;
for (i = 0; i < (width * height); i++) {
fwrite(&imgData[i], 3, sizeof(char), bitmap);
if ((i + 1) % width == 0) { /* if at end of scanline */
for(p = 0; p < (width % 4); p++) {
fputc('\0', bitmap); /* append appropriate # of padding bits */
}
}
}