简体   繁体   English

从RGB值数组生成BMP文件

[英]Generate BMP file from array of RGB values

I would like to generate a BMP file from RGB values that I have stored already. 我想从已经存储的RGB值生成一个BMP文件。

I'm programming on OS X so I can't use the predefined BMP headers. 我在OS X上进行编程,因此无法使用预定义的BMP标头。

I've tried doing the below but preview says that the file is corrupted. 我尝试执行以下操作,但预览显示文件已损坏。

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);
    }
}
}

I'm not sure that I understand the structure correctly. 我不确定我是否正确理解结构。 I've tried reading about it but I must be missing something. 我已经尝试阅读有关它的内容,但是我一定会丢失一些东西。

Here is the hex output of the 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 0 42 4D 00 02 38 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 00 0 0 0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

The question is good because it enlightens some pitfalls. 这个问题很好,因为它启发了一些陷阱。 Here are all issues in your code. 这是您代码中的所有问题。 By fixing them, everything works as expected: 通过修复它们,一切都会按预期工作:

  • Your bfSize field does not include the size of the bitmap 您的bfSize字段不包括位图的大小

  • General: The BITMAPFILEHEADER becomes to large, since it begins with a 2 byte value followed by a 4 byte value. 常规: BITMAPFILEHEADER变大,因为它以2字节值开头,然后是4字节值。 Padding rules say that this struct the first field will be 4 bytes instead of 2. Solution: Write the magic number separately by excluding it from BITMAPFILEHEADER : 填充规则说,此结构的第一个字段将是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...) 

    This modification also implies that BITMAPFILEHEADER::bfSize is 2+sizeof(BITMAPFILEHADER) + sizeof(BITMAPINFOHEADER)+ biWidth*biHeight*3 此修改还意味着BITMAPFILEHEADER::bfSize2+sizeof(BITMAPFILEHADER) + sizeof(BITMAPINFOHEADER)+ biWidth*biHeight*3

  • You have also passed sizeof(bfh) for booth bfh and bih , so the complete BITMAPINFOHEADER is never written 您还通过了bfhbih展位的sizeof(bfh) ,因此永远不会编写完整的BITMAPINFOHEADER

  • The BMP file format requires that each scanline is DWORD-aligned so depending on the width, you may need to write zero bytes after each scanline BMP文件格式要求每条扫描线都是DWORD对齐的,因此根据宽度,您可能需要在每条扫描线后写入零字节

  • If you think the first scanline is up, your image is upside down, since your height is positive. 如果您认为第一条扫描线朝上,则图像为上下颠倒,因为您的身高为正。

  • Bitmap files are stored row -wise, so you should loop over x -coordinates in the innermost loop 位图文件按存储,因此您应该在最内部的循环中遍历x坐标

  • You have also write the wrong pixel values. 您还写入了错误的像素值。 You should write unsigned char instead of float. 您应该写unsigned char而不是float。 You write the correct size (1 byte), but it will not be the correct value: 您写入正确的大小(1个字节),但它不是正确的值:

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

    Also, bitmaps are BGR(A) and not RGB(A). 同样,位图是BGR(A)而不是RGB(A)。

Assuming you have a nice width, a working example is 假设您的宽度不错,一个有效的例子是

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's Code worked for me. user877329的代码对我有用。 (Just some minor changes because im using C) Note: Just pay attention, to the 'size' of the Variables, because it can be different, depending on which platform you Compile. (因为我使用C语言,所以进行了一些小的更改)注意:请注意变量的“大小”,因为变量的大小可能不同,具体取决于您编译的平台。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM