用C语言创建bmp文件

用C语言创建bmp文件,c,bmp,C,Bmp,我正在尝试创建.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 tagBITMAPFILE

我正在尝试创建.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;
}
应该是

bfh.bfType = 0x4D42;

但仍然看不到图像。

我认为您的字段大小不正确,请尝试此操作

#pragma pack(push, 1)

typedef struct tagBITMAPFILEHEADER {
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
} BITMAPFILEHEADER;

#pragma pack(pop)
首先,您设置:

bfSize
指定文件的大小,以字节为单位

对于无效值,您的代码导致
1440112
,而文件的大小实际上是
1080112

bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) +
    bih.biWidth * bih.biHeight * sizeof(COLORREF_RGB);    
因为
sizeof(COLORREF\u 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

您的偏移量与上的偏移量不匹配,您使用的引用可能是为16位操作系统上的16位编译器编写的(正如所指出的),因此它假定
int
为2B类型

使用
stdint.h
中的值对我有效(Visual Studio的
stdint.h
指南):

#包括
类型定义单元16_t单元;
类型定义uint32_t DWORD;
typedef int32t长;
typedef uint16_t字;
typedef uint8_t字节;

最后但并非最不重要的一点是,您必须关闭内存对齐,因为。

您试图将C结构映射到一些外部定义的二进制格式。这方面存在许多问题:

大小 C语言只指定这些类型的最小大小。它们的实际大小在不同的编译器和操作系统之间可能也确实有所不同。结构中构件的大小和偏移量可能不是您所期望的。您可以依赖的唯一大小(在您现在可能遇到的几乎任何系统上)是
char
为8位

衬垫 通常情况下,
DWORD
的大小是
UINT
的两倍,实际上您的程序依赖于它。这通常意味着编译器将在
bfType
bfSize
之间引入填充,以便后者对其类型进行适当的对齐。这个系统没有这样的填充

命令 另一个问题是C语言没有指定类型的endianess。
.bfType
成员可以作为
[42][4D]
(大端)或
[4D][42]
(小端)存储在内存中。特别要求以小端顺序存储值

解决方案 通过使用特定于编译器的扩展(例如
#pragma
或编译器开关),您可能可以解决其中一些问题,但可能不是所有问题

正确写入外部定义的二进制格式的唯一方法是使用
无符号字符数组
,每次写入一个字节的值。就我个人而言,我会为特定类型编写一组助手函数:

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+ 2, bfh.bfSize);
  w16LE (buf+ 6, bfh.bfReserved1);
  w16LE (buf+ 8, bfh.bfReserved2);
  w32LE (buf+10, bfh.bfOffBits);

  return fwrite (buf, sizeof buf, 1, f);
}

这里看起来像是结构成员对齐问题。如果在声明结构之前放置
#pragma pack(1)
,会发生什么情况?现在我得到了“从…读取BMP文件头时出错”的消息。请确保对多字节值使用正确的字节顺序。您引用的错误不可能出现在您在问题中粘贴的代码中。您是如何得到它的?typedef unsigned int UINT在64位上很可能与32位上的大小不同。它仍然是一样的。我用它作为我的主要参考,他们说它应该是
unsigned int
。它没有说
unsigned int
,而是说
UINT
,在那页的哪里定义了
UINT
?我不在乎你的页面怎么说,我编译并运行了你的程序,修改了这里的注释,它创建了一个600x600黑色24位位图,我的图像查看器可以显示。你有没有包括@SirDarius在第一条注释中推荐的
pack
pragmas(我在你最近的注释后添加到我的答案中)?奇怪的事情,但我只是在我的windows PC上编译了它,它在那里工作。正如我在文章中提到的,我使用的是Ubuntu 14.04。定义一个具有特定于机器的字段大小的可移植文件格式是不允许的,请参阅@Matt的评论。正如旁注:bmp是16位windows使用的格式,这就是为什么UINT是uint16\t。在16位编译器上,int为16位。
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;
BITMAPFILEHEADER bfh;

bfh.bfType = 0x424D;
...
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, f);
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 */
int function write_header (FILE *f, BITMAPFILEHEADER bfh)
{
  unsigned char buf[14];

  w16LE (buf   , bfh.bfType);
  w32LE (buf+ 2, bfh.bfSize);
  w16LE (buf+ 6, bfh.bfReserved1);
  w16LE (buf+ 8, bfh.bfReserved2);
  w32LE (buf+10, bfh.bfOffBits);

  return fwrite (buf, sizeof buf, 1, f);
}