在 C 中创建简单的位图(没有外部库)



出于学习目的,我想使用 C 编程语言创建单个 2x2 位图图像,而无需外部库(如 SDL(。

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

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

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

位图中的像素数据本质上是一个一维字节数组。如果没有标头信息,则无法解释此数据,该标头信息显示宽度,高度,位计数和有关位图的其他信息。

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

标题还包括由于历史原因而存在的其他信息。这对学习 C 没有什么价值。你只需要接受它。

标头信息位于两个结构中 BITMAPFILEHEADERBITMAPINFO 它稍微复杂一些,因为结构应该被打包。

像素数据取决于位图格式。没有前面提到的单一位图格式。下面的示例显示了一个 24 位位图。

其他奇怪的是每行的宽度应始终是 4 个字节的倍数。这称为填充。此外,位图行从底部开始,而不是从顶部开始。这在其他资源中有解释

#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请使用此版本。

#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 properlyn");
        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 个字节...

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 字节长的结构:

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的数字的奥秘

#include<stdio.h>
#include<conio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include <stdint.h>
 long asciiToBinary(int n);
 long asciiToBinary(int n) {
    int remainder; 
 long binary = 0, i = 1;
   while(n != 0) {
        remainder = n%2;
        n = n/2;
        binary= binary + (remainder*i);
        i = i*10;
                 }
    return binary;
                           }

typedef struct
{ //BITMAPFILEHEADER
  char  bfType[2];
  int   bfSize;
  short bfReserved1;
  short bfReserved2;
  int   bfOffBits;
  //BITMAPINFOHEADER
  int   biSize;
  int   biWidth;
  int   biHeight;
  short biPlanes;
  short biBitCount;
  int   biCompression;
  int   biSizeImage;
  int   biXPelsPerMeter;
  int   biYPelsPerMeter;
  int   biClrUsed;
  int   biClrImportant;
} BITMAPINFOHEADER;
 typedef struct {
   unsigned char blue;
   unsigned char green;
   unsigned char red;
   
} PIXEL;
int imageInfo(BITMAPINFOHEADER *img)
{
  int offset = 0;
  printf("------------------------------------------------------------- BITMAPFILEHEADERn");
  printf("0x%08X (%08d) bfType           %8c%c (0x%04X)n", offset, offset, img->bfType[0], img->bfType[1], *(short *)img->bfType);
  offset += sizeof img->bfType;
  printf("0x%08X (%08d) bfSize           %9d (0x%08X)n",   offset, offset,  img->bfSize, img->bfSize);
  offset += sizeof img->bfSize;
  printf("0x%08X (%08d) bfReserved1      %9hd (0x%04hX)n", offset, offset,  img->bfReserved1, img->bfReserved1);
  offset += sizeof img->bfReserved1;
  printf("0x%08X (%08d) bfReserved2      %9hd (0x%04hX)n", offset, offset,  img->bfReserved2, img->bfReserved2);
  offset += sizeof img->bfReserved2;
  printf("0x%08X (%08d) bfOffBits        %9d (0x%08X)n",   offset, offset,  img->bfOffBits, img->bfOffBits);
  printf("------------------------------------------------------------- BITMAPINFOHEADERn");
  offset += sizeof img->bfOffBits;
  printf("0x%08X (%08d) biSize           %9d (0x%08X)n",   offset, offset, img->biSize, img->biSize);
  offset += sizeof img->biSize;
  printf("0x%08X (%08d) biWidth          %9d (0x%08X)n",   offset, offset, img->biWidth, img->biWidth);
  offset += sizeof img->biWidth;
  printf("0x%08X (%08d) biHeight         %9d (0x%08X)n",   offset, offset, img->biHeight, img->biHeight);
  offset += sizeof img->biHeight;
  printf("0x%08X (%08d) biPlanes         %9d (0x%04hX)n",  offset, offset, img->biPlanes, img->biPlanes);
  offset += sizeof img->biPlanes;
  printf("0x%08X (%08d) biBitCount       %9d (0x%04hX)n",  offset, offset, img->biBitCount, img->biBitCount);
  offset += sizeof img->biBitCount;
  printf("0x%08X (%08d) biCompression    %9d (0x%08X)n",   offset, offset, img->biCompression, img->biCompression);
  offset += sizeof img->biCompression;
  printf("0x%08X (%08d) biSizeImage      %9d (0x%08X)n",   offset, offset, img->biSizeImage, img->biSizeImage);
  offset += sizeof img->biSizeImage;
  printf("0x%08X (%08d) biXPelsPerMeter  %9d (0x%08X)n",   offset, offset, img->biXPelsPerMeter, img->biXPelsPerMeter);
  offset += sizeof img->biXPelsPerMeter;
  printf("0x%08X (%08d) biYPelsPerMeter  %9d (0x%08X)n",   offset, offset, img->biYPelsPerMeter, img->biYPelsPerMeter);
  offset += sizeof img->biYPelsPerMeter;
  printf("0x%08X (%08d) biClrUsed        %9d (0x%08X)n",   offset, offset, img->biClrUsed, img->biClrUsed);
  offset += sizeof img->biClrUsed;
  printf("0x%08X (%08d) biClrImportant   %9d (0x%08X)n",   offset, offset, img->biClrImportant, img->biClrImportant);
  return offset;
}
int main(){
    uint32_t RGB565ColorTable[] = {
        0x7E00000, 0xF8000000, 0x001F0000, 0
    };
  uint16_t myData[64];
    for(int i = 0; i<64; i++)
    {
        myData[i] = 0x241B; //Random Color
    }
    FILE *image;
      const char filename[] = "full.bmp";
  FILE *fichier = fopen(filename, "rb");
  FILE *fptr = fopen("tth.bmp","w+");
    if(!fichier)
    printf ("Erreur a l'ouverture du fichier [%s]n", filename);
  else
  {
    BITMAPINFOHEADER *img = malloc(sizeof *img);
    char fpath[1000],mydata[100];
    BITMAPINFOHEADER bih;
    int i=0,b[8],g[8],r[8];
    double asciiTobinary;
                      
    image=fopen("full.bmp","rb");
   
    fseek(image,2,SEEK_SET);                                                //reading the height and width
    fread(&bih.biSize,4,1,image);
    printf("n n Size of the image=%dn",bih.biSize);
    fseek(image,18,SEEK_SET);
    fread(&bih.biWidth,4,1,image);
    fseek(image,22,SEEK_SET);
    fread(&bih.biHeight,4,1,image);
    printf("n n Width of the image =%d n Height of the image =%d n pixel =  b    |    g     |     r n n",bih.biWidth,bih.biHeight);
     PIXEL pic[bih.biWidth*bih.biHeight*2],p; 
     
      while(!feof(image)){                                                 //reading the pixels rgb values
      fread(&p.blue,sizeof(p.blue),1,image);                              
      fread(&p.green,sizeof(p.green),1,image);
      fread(&p.red,sizeof(p.red),1,image);
      pic[i]=p;
      printf(" %d=   %u    |     %u    |      %u  ",i+54,pic[i].blue,pic[i].green,pic[i].red);
      i++; }
             
     fclose(image);
if(img)
    {
      int i, tmp, offset;
      printf("n[%s]n", filename);
      fread(img, sizeof(BITMAPINFOHEADER), 1, fichier);
      if(img->bfType[0] == 'B' && img->bfType[1] == 'M')
      {
        offset = imageInfo(img);
        if(img->biBitCount == 1)
        {
          printf("---------------------------------------------------------------------- RGBQUADn");
          fread(&tmp, sizeof tmp, 1, fichier);
          offset += sizeof img->biClrImportant;
          printf("0x%08X (%08d)                  %9d (0x%08X)n", offset, offset, tmp, tmp);
          fread(&tmp, sizeof tmp, 1, fichier);
          offset += sizeof tmp;
          printf("0x%08X (%08d)                  %9d (0x%08X)n", offset, offset, tmp, tmp);
          printf("------------------------------------------------------------------------- BITSn");
          for(i = 0; i < img->biSizeImage / (int)sizeof tmp; i++)
          {
              char bin[33];
            fread(&tmp, sizeof tmp, 1, fichier);
            offset += sizeof tmp;
            printf("0x%08X (%08d)                   %8d (0x%08X) (%s)n", offset, offset, tmp, tmp, itoa(tmp, bin, 2));
          }
        }
      }
      free(img);
    }
    fclose(fichier);
  }
     if(fptr == NULL) {
        printf("Error!");
        exit(1);
    }
    fwrite(&filename, sizeof(filename), 1, fptr);
    //fwrite(&myInfoHeader, sizeof(myInfoHeader), 1, fptr);
    fwrite(&RGB565ColorTable, sizeof(RGB565ColorTable), 1, fptr);
    fwrite(&myData, sizeof(myData), 1, fptr);
    fclose(fptr);
     return 0;
}

最新更新