我正在尝试创建.bmp文件(填充一种颜色用于测试目的)。
以下是我正在使用的代码:
#include <stdio.h>
#define BI_RGB 0
typedef unsigned int UINT;
typedef unsigned long DWORD;
typedef long int LONG;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef struct tagBITMAPFILEHEADER {
UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
typedef struct COLORREF_RGB
{
BYTE cRed;
BYTE cGreen;
BYTE cBlue;
}COLORREF_RGB;
int main(int argc, char const *argv[])
{
BITMAPINFOHEADER bih;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = 600;
bih.biHeight = 600;
bih.biSizeImage = bih.biWidth * bih.biHeight * 3;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = BI_RGB;
bih.biXPelsPerMeter = 2835;
bih.biYPelsPerMeter = 2835;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
COLORREF_RGB rgb;
rgb.cRed = 0;
rgb.cGreen = 0;
rgb.cBlue = 0;
BITMAPFILEHEADER bfh;
bfh.bfType = 0x424D;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + bih.biSize;
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
bih.biWidth * bih.biHeight * 4;
FILE *f;
f = fopen("test.bmp","wb");
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, f);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, f);
int i,j;
for(i = 0; i < bih.biHeight; i++)
{
for(j = 0; j < bih.biWidth; j++)
{
fwrite(&rgb,sizeof(COLORREF_RGB),1,f);
}
}
fclose(f);
return 0;
}
并且每次编译并运行它时都会出现错误我得到的错误是它的无效BMP图像。我仔细检查了多个参考文献中的所有值,但仍然无法在此处找到错误。
我误解了什么,我在这里做错了什么?
也不确定是否重要,但我正在使用Ubuntu 14.04来编译它。
修改的
又发现了一个问题:
bfh.bfType = 0x424D;
应该是
bfh.bfType = 0x4D42;
但仍然无法看到图像。
答案 0 :(得分:4)
首先,你设置:
bfSize
指定文件的大小(以字节为单位)。
为无效值,您的代码产生1440112
,而文件大小实际为1080112
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
bih.biWidth * bih.biHeight * sizeof(COLORREF_RGB);
因为sizeof(COLORREF_RGB)
实际上是4而不是3。
另一个错误是你的结构和类型的大小:
expected size actual size*
typedef unsigned int UINT; // 2 4
typedef unsigned long DWORD; // 4 8
typedef long int LONG; // 4 8
typedef unsigned short WORD; // 2 2
typedef unsigned char BYTE; // 1 1
*我在x86_64架构上使用gcc
您的偏移量与wikipedia上的偏移量不匹配,您正在使用的引用可能是针对16位操作系统上的16位编译器编写的(如cup in a comment所指出的)因此它假定{{ 1}}是2B型。
使用int
中的值为我工作(Visual Studio here的stdint.h
指南):
stdint.h
最后但并非最不重要的是,您必须将内存对齐关闭为suggested by Weather Vane。
答案 1 :(得分:2)
我相信您的字段大小不正确,请尝试此
#pragma pack(push, 1)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop)
答案 2 :(得分:2)
您正在尝试将C结构映射到某种外部定义的二进制格式这有很多问题:
typedef unsigned int UINT; typedef unsigned long DWORD; typedef long int LONG; typedef unsigned short WORD; typedef unsigned char BYTE;
C语言仅指定这些类型的最小大小。它们的实际大小可以并且确实在不同的编译器和操作系统之间变化。结构中构件的尺寸和偏移量可能与您的预期不符。您可以依赖的唯一大小(几乎您可能会遇到的任何系统)char
为8位。
typedef struct tagBITMAPFILEHEADER { UINT bfType; DWORD bfSize; UINT bfReserved1; UINT bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER;
DWORD
通常是UINT
的两倍,事实上你的程序依赖于它。这通常意味着编译器将在bfType
和bfSize
之间引入填充,以使后者对其类型进行适当的对齐。 .bmp
format没有这样的填充。
BITMAPFILEHEADER bfh; bfh.bfType = 0x424D; ... fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, f);
另一个问题是C语言没有指定类型的 endianess 。 .bfType
成员可以在内存中存储为[42][4D]
(Big-Endian)或[4D][42]
(Little-Endian)。 .bmp
format特别要求以Little-Endian顺序存储值。
您可能能够通过使用特定于编译器的扩展(例如#pragma
或编译器开关)解决某些这些问题,但可能不是全部。
正确编写外部定义的二进制格式的唯一方法是使用unsigned char
数组并一次写入一个字节的值。就个人而言,我会为特定类型编写一组辅助函数:
void w32BE (unsigned char *p, unsigned long ul)
{
p[0] = (ul >> 24) & 0xff;
p[1] = (ul >> 16) & 0xff;
p[2] = (ul >> 8) & 0xff;
p[3] = (ul ) & 0xff;
}
void w32LE (unsigned char *p, unsigned long ul)
{
p[0] = (ul ) & 0xff;
p[1] = (ul >> 8) & 0xff;
p[2] = (ul >> 16) & 0xff;
p[3] = (ul >> 24) & 0xff;
}
/* And so on */
以及用于编写整个.bmp
文件或部分文件的一些函数:
int function write_header (FILE *f, BITMAPFILEHEADER bfh)
{
unsigned char buf[14];
w16LE (buf, bfh.bfType);
w32LE (buf, bfh.bfSize);
w16LE (buf, bfh.bfReserved1);
w16LE (buf, bfh.bfReserved2);
W32LE (buf, bfh.bfOffBits);
return fwrite (buf, sizeof buf, 1, f);
}