从 RGB 值数组生成 BMP 文件

Generate BMP file from array of RGB values

我想从我已经存储的 RGB 值生成一个 BMP 文件。

我在 OS X 上编程,所以我不能使用预定义的 BMP headers。

我已尝试执行以下操作,但预览显示文件已损坏。

void bitmap(Image * image) {

typedef struct                       /**** BMP file header structure ****/
{
    unsigned short bfType;           /* Magic number for file */
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BITMAPFILEHEADER;

typedef struct                       /**** BMP file info structure ****/
{
    unsigned int   biSize;           /* Size of info header */
    int            biWidth;          /* Width of image */
    int            biHeight;         /* Height of image */
    unsigned short biPlanes;         /* Number of color planes */
    unsigned short biBitCount;       /* Number of bits per pixel */
    unsigned int   biCompression;    /* Type of compression to use */
    unsigned int   biSizeImage;      /* Size of image data */
    int            biXPelsPerMeter;  /* X pixels per meter */
    int            biYPelsPerMeter;  /* Y pixels per meter */
    unsigned int   biClrUsed;        /* Number of colors used */
    unsigned int   biClrImportant;   /* Number of important colors */
} BITMAPINFOHEADER;

BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;

bfh.bfType = 0x4d42;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
bfh.bfOffBits = 0x36;

bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = image->getWidth();
bih.biHeight = image->getHeight();
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0x0ec4;
bih.biYPelsPerMeter = 0x0ec4;
bih.biClrUsed = 0;
bih.biClrImportant = 0;

FILE *file = fopen("a.bmp", "wb");
if (!file) {
    cout << "File not found";
    return;
}

fwrite(&bfh, 1, sizeof(bfh), file);
fwrite(&bih, 1, sizeof(bfh), file);

for (int x = 0; x < image->getWidth(); x++) {
    for (int y = 0; y < image->getHeight(); y++) {
        float r = image->getPixel(x, y).r;
        float g = image->getPixel(x, y).g;
        float b = image->getPixel(x, y).b;
        fwrite(&r, 1, 1, file);
        fwrite(&g, 1, 1, file);
        fwrite(&b, 1, 1, file);
    }
}
}

我不确定我是否正确理解了结构。我试过阅读它,但我一定遗漏了一些东西。

这是文件的十六进制输出

42 4D 00 02 38 00 00 00 00 00 00 00 36 00 00 00 28 00 00 00 80 02 00 00 E0 01 00 00 01 00 18 00 00 00 00 00 00 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

这个问题很好,因为它启发了一些陷阱。这是您的代码中的所有问题。通过修复它们,一切都按预期工作:

  • 您的 bfSize 字段不包括位图的大小

  • 一般:BITMAPFILEHEADER 变得很大,因为它以 2 字节值开头,后跟 4 字节值。填充规则说这个结构的第一个字段将是 4 个字节而不是 2 个字节。解决方案:通过将幻数从 BITMAPFILEHEADER 中排除来单独写入幻数:

    unsigned short magic=0x4d42; //This field is _not_ included in `BITMAPFILEHEADER`
    fwrite(&magic,1,sizeof(magic),file);
    
    // Write the remaining part of the header (if you did not get an I/O error...)
    

    这个修改也意味着BITMAPFILEHEADER::bfSize2+sizeof(BITMAPFILEHADER) + sizeof(BITMAPINFOHEADER)+ biWidth*biHeight*3

  • 你也通过了sizeof(bfh)展位bfhbih,所以完整的BITMAPINFOHEADER永远不会写

  • BMP 文件格式要求每个扫描线都是 DWORD 对齐的,因此根据宽度,您可能需要在每个扫描线后写入零字节

  • 如果你认为第一条扫描线是向上的,那么你的图像是颠倒的,因为你的高度是正数。

  • 位图文件存储在-wise,所以你应该在最内层循环中遍历x-坐标

  • 你也写错了像素值。你应该写 unsigned char 而不是 float。您写入了正确的大小(1 个字节),但它不会是正确的值:

    unsigned char r = image->getPixel(x, y).r/255; //If 1.0f is white.
    

    此外,位图是 BGR(A) 而不是 RGB(A)。

假设你有一个合适的宽度,一个有效的例子是

void bitmap()
{
typedef struct                       /**** BMP file header structure ****/
    {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
    } BITMAPFILEHEADER;

typedef struct                       /**** BMP file info structure ****/
    {
    unsigned int   biSize;           /* Size of info header */
    int            biWidth;          /* Width of image */
    int            biHeight;         /* Height of image */
    unsigned short biPlanes;         /* Number of color planes */
    unsigned short biBitCount;       /* Number of bits per pixel */
    unsigned int   biCompression;    /* Type of compression to use */
    unsigned int   biSizeImage;      /* Size of image data */
    int            biXPelsPerMeter;  /* X pixels per meter */
    int            biYPelsPerMeter;  /* Y pixels per meter */
    unsigned int   biClrUsed;        /* Number of colors used */
    unsigned int   biClrImportant;   /* Number of important colors */
    } BITMAPINFOHEADER;

BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;

/* Magic number for file. It does not fit in the header structure due to alignment requirements, so put it outside */
unsigned short bfType=0x4d42;           
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfSize = 2+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+640*480*3;
bfh.bfOffBits = 0x36;

bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biWidth = 640;
bih.biHeight = 480;
bih.biPlanes = 1;
bih.biBitCount = 24;
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 5000;
bih.biYPelsPerMeter = 5000;
bih.biClrUsed = 0;
bih.biClrImportant = 0;

FILE *file = fopen("a.bmp", "wb");
if (!file)
    {
    printf("Could not write file\n");
    return;
    }

/*Write headers*/
fwrite(&bfType,1,sizeof(bfType),file);
fwrite(&bfh, 1, sizeof(bfh), file);
fwrite(&bih, 1, sizeof(bih), file);

/*Write bitmap*/
for (int y = bih.biHeight-1; y>=0; y--) /*Scanline loop backwards*/
    {
    for (int x = 0; x < bih.biWidth; x++) /*Column loop forwards*/
        {
        /*compute some pixel values*/
        unsigned char r = 255*((float)x/bih.biWidth);
        unsigned char g = 255*((float)y/bih.biHeight);
        unsigned char b = 0;
        fwrite(&b, 1, 1, file);
        fwrite(&g, 1, 1, file);
        fwrite(&r, 1, 1, file);
        }
    }
fclose(file);
}

user877329 的代码对我有用。 (只是一些小改动,因为我使用的是 C) 注意:请注意,Variables 的 'size',因为它可能会有所不同,具体取决于您编译的平台。