在C中创建简单的位图(无需外部库) [英] Create simple bitmap in C (without external libraries)

查看:116
本文介绍了在C中创建简单的位图(无需外部库)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

出于学习目的,我希望使用不带外部库(例如SDL)的C编程语言创建单个2x2位图图像.

For learning purposes, I want to create a single 2x2 bitmap image using C programming language with no external libraries (such as SDL).

我已经读到我需要为位图文件创建一个头文件,但是无法理解如何在代码中实现它以及为什么bmp头文件具有这些参数.我找不到任何有关如何使用C的清晰教程.

I've read that I need to create a header for the bitmap file but failed to understand how to implement it in code and why does the bmp header have those parameters. I can't find any clear tutorial on how I could do it using C.

您能否通过一个简单的文档示例为我提供一些有关如何实现此目标的指导?我正在使用Linux.

Could you provide me some guidance on how to implement this by a simple documented example? I'm using Linux.

推荐答案

为什么bmp标头具有这些参数

位图中的像素数据本质上是字节的一维数组.没有标题信息就无法解释该数据,而标题信息会显示宽度,高度,位数和其他有关位图的信息.

The pixel data in bitmap is essentially a one-dimensional array of bytes. It's impossible to interpret this data without header information which reveals width, height, bit-count and other information about the bitmap.

位图的类型不同,包括不同的调色板格式和非调色板的16位,24位或32位,以及其中某些组中的不同类型.

There are different types of bitmaps, including different palette format and non-palette 16, 24, or 32 bits, and different types within some of these groups.

标头还包括其他由于历史原因而存在的信息.这对学习C毫无价值.您只需接受它即可.

The header also includes other information which are there for historical reasons. This has little value for learning C. You just have to accept it.

标头信息采用两种结构 BITMAPINFO 稍微复杂一点,因为预计结构会被打包.

The header information is in two structures BITMAPFILEHEADER and BITMAPINFO It's a little more complicated because the structures are expected to be packed.

像素数据取决于位图格式.如前所述,没有一种位图格式.下面的示例显示了一个24位的位图.

The pixel data depends on the bitmap format. There is not a single bitmap format as mentioned earlier. The example below shows a 24-bit bitmap.

其他奇怪的是,每行的宽度应始终为4字节的倍数.这称为填充.位图行也从底部开始,而不是从顶部开始.其他资源

Other oddities is that the width of each row should always be multiple of 4 bytes. This called padding. Also the bitmap rows start at the bottom, not at the top. This is explained in other resources

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main(void)
{
    //width, height, and bitcount are the key factors:
    int32_t width = 2;
    int32_t height = 2;
    uint16_t bitcount = 24;//<- 24-bit bitmap

    //take padding in to account
    int width_in_bytes = ((width * bitcount + 31) / 32) * 4;

    //total image size in bytes, not including header
    uint32_t imagesize = width_in_bytes * height;

    //this value is always 40, it's the sizeof(BITMAPINFOHEADER)
    const uint32_t biSize = 40;

    //bitmap bits start after headerfile, 
    //this is sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
    const uint32_t bfOffBits = 54; 

    //total file size:
    uint32_t filesize = 54 + imagesize;

    //number of planes is usually 1
    const uint16_t biPlanes = 1;

    //create header:
    //copy to buffer instead of BITMAPFILEHEADER and BITMAPINFOHEADER
    //to avoid problems with structure packing
    unsigned char header[54] = { 0 };
    memcpy(header, "BM", 2);
    memcpy(header + 2 , &filesize, 4);
    memcpy(header + 10, &bfOffBits, 4);
    memcpy(header + 14, &biSize, 4);
    memcpy(header + 18, &width, 4);
    memcpy(header + 22, &height, 4);
    memcpy(header + 26, &biPlanes, 2);
    memcpy(header + 28, &bitcount, 2);
    memcpy(header + 34, &imagesize, 4);

    //prepare pixel data:
    unsigned char* buf = malloc(imagesize);
    for(int row = height - 1; row >= 0; row--)
    {
        for(int col = 0; col < width; col++)
        {
            buf[row * width_in_bytes + col * 3 + 0] = 255;//blue
            buf[row * width_in_bytes + col * 3 + 1] = 0;//green
            buf[row * width_in_bytes + col * 3 + 2] = 0;//red
        }
    }

    FILE *fout = fopen("test.bmp", "wb");
    fwrite(header, 1, 54, fout);
    fwrite((char*)buf, 1, imagesize, fout);
    fclose(fout);
    free(buf);

    return 0;
}

您可以使用结构重写此代码.在此代码结构中,包含了#pragma pack(push, 1),后者与编译器相关.如果#pragma指令有效,请使用此版本.

You can rewrite this code using structures. In this code structure is packed with #pragma pack(push, 1) which is compiler dependent. Use this version if #pragma directive works.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#pragma pack(push, 1)
struct my_BITMAPFILEHEADER {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffBits;
};

struct my_BITMAPINFOHEADER {
    uint32_t biSize;
    int32_t  biWidth;
    int32_t  biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    int32_t  biXPelsPerMeter;
    int32_t  biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
};
#pragma pack(pop)

int main(void)
{
    if(sizeof(struct my_BITMAPFILEHEADER) != 14 &&
        sizeof(struct my_BITMAPINFOHEADER) != 40)
    {
        printf("bitmap structures not packed properly\n");
        return 0;
    }

    //only width and height can be changed in this code:
    int width = 2;
    int height = 2;

    int bitcount = 24;//<- 24-bit bitmap
    int width_in_bytes = ((width * bitcount + 31) / 32) * 4;    //for padding
    uint32_t imagesize = width_in_bytes * height;   //total image size

    struct my_BITMAPFILEHEADER filehdr = { 0 };
    struct my_BITMAPINFOHEADER infohdr = { 0 };

    memcpy(&filehdr, "BM", 2);//bitmap signature
    filehdr.bfSize = 54 + imagesize;//total file size
    filehdr.bfOffBits = 54; //sizeof(filehdr) + sizeof(infohdr)

    infohdr.biSize = 40; //sizeof(infohdr)
    infohdr.biPlanes = 1; //number of planes is usually 1
    infohdr.biWidth = width;
    infohdr.biHeight = height;
    infohdr.biBitCount = bitcount;
    infohdr.biSizeImage = imagesize;

    //prepare pixel data:
    unsigned char* buf = malloc(imagesize);
    for(int row = height - 1; row >= 0; row--)
    {
        for(int col = 0; col < width; col++)
        {
            buf[row * width_in_bytes + col * 3 + 0] = 255;//blue
            buf[row * width_in_bytes + col * 3 + 1] = 0;//red
            buf[row * width_in_bytes + col * 3 + 2] = 0;//green
        }
    }

    FILE *fout = fopen("test.bmp", "wb");
    fwrite(&filehdr, sizeof(filehdr), 1, fout);
    fwrite(&infohdr, sizeof(infohdr), 1, fout);
    fwrite((char*)buf, 1, imagesize, fout);
    fclose(fout);
    free(buf);

    return 0;
}

第一个版本使用unsigned char header[54]来实现相同的目的.查看每个数据的大小及其在内存中的相对位置.结构为14个字节长. bfType从0开始,为2个字节长(16位). bfSize从位置2开始,长度为4个字节...

The first version used unsigned char header[54] to achieve the same thing. See the size of each data and its relative position in memory. The structure is 14 bytes long. bfType starts at 0, and is 2 bytes long (16 bits). bfSize starts at position 2, and is 4 bytes long...

struct my_BITMAPFILEHEADER {
    uint16_t bfType; //starts at 0, 2 bytes long
    uint32_t bfSize; //starts at 2, 4 bytes long
    uint16_t bfReserved1; //starts at 6, 2 bytes long
    uint16_t bfReserved2; //starts at 8, 2 bytes long
    uint32_t bfOffBits; //starts at 10, 4 bytes long
};

然后您将拥有40个字节长的下一个结构:

Then you have the next structure which is 40 bytes long:

struct my_BITMAPINFOHEADER {
    uint32_t biSize;//starts at 14
    int32_t  biWidth;//starts at 18
    int32_t  biHeight;//starts at 22
    uint16_t biPlanes;//starts at 26
    uint16_t biBitCount;//starts at 28
    uint32_t biCompression;//starts at 30
    uint32_t biSizeImage;//starts at 34
    int32_t  biXPelsPerMeter;
    int32_t  biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
};

biSize从0开始.但是以前的结构是14个字节.因此biSzie14开始.这应该揭示出先前版本中用于将整数复制到header

biSize starts at 0. But the previous structure was 14 bytes. So biSzie starts at 14. This should reveal the mystery of the numbers used in previous version to copy integers to header

这篇关于在C中创建简单的位图(无需外部库)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆