C語言 控制台程序生成BMP圖片


首先,本篇文章參考了眾多博客,有:

http://blog.csdn.net/embededvc/article/details/6737751

http://blog.chinaunix.net/uid-22277851-id-1777698.html

http://www.cnblogs.com/lzlsky/archive/2012/08/16/2641698.html

http://blog.csdn.net/lyy289065406/article/details/6717679

http://blog.csdn.net/carson2005/article/details/7614125 等等,通過概括,然后做出了一個可以生成4位深以及24位深bmp圖片的程序(通過控制台來選擇),並加了一些自己的感悟而成

(一)BMP圖像格式

  首先,BMP文件可分為四部分

位圖文件頭(BITMAPFILEHEADER)  位圖信息頭(BITMAPINFOHEADER) 顏色表(RgbQuad) 文件數據(DATA)

但是,有時候在處理圖像時(如使用StretchDIBits函數時),需要一個BITMAPINFO結構,BITMAPINFO其實就是上面的位圖文件頭和顏色表的組合,如下

typedef struct tagBITMAPINFO // bmi
{
BITMAPINFOHEADER bmiHeader ; // info-header structure
RGBQUAD bmiColors[1] ; // color table array
}
BITMAPINFO, * PBITMAPINFO ;

因此,BMP文件就被分成了三部分

位圖文件頭(BITMAPFILEHEADER) 位圖信息(BITMAPINFO) 文件數據(DATA)

但是呢,還有一種情況,就是位深為24時,是真彩色圖片(下面會提到),此時沒有顏色表,自身文件數據就能表示顏色。(下面也會詳細說),依舊為三部分,如下

位圖文件頭(BITMAPFILEHEADER)  位圖信息頭(BITMAPINFOHEADER) 文件數據(DATA)

 

(二)位圖文件頭(BITMAPFILEHEADER)

    位圖文件頭(bitmap-file header)包含了圖像類型、圖像大小、圖像數據存放地址和兩個保留未使用的字段。

  (1)BITMAPCOREHEADER

typedef struct tagBITMAPCOREHEADER // bmch
{
DWORD bcSize ; // size of the structure = 12
WORD bcWidth ; // width of image in pixels
WORD bcHeight ; // height of image in pixels
WORD bcPlanes ; // = 1
WORD bcBitCount ; // bits per pixel (1, 4, 8, or 24)
}
BITMAPCOREHEADER, * PBITMAPCOREHEADER ;

  bfType  圖片的類型 必須是BM 填0x4d42即十進制的19778

    bfOffBits 從文件頭開始到顏色數據結束的偏移量  54+sizeof(RGBQUAD)*256

    bfSize  圖片的大小,bfOffBits + 長 X 寬 X 位數  

      例如:對於1*3 4位的圖像  bfSize=1*3 + 54+sizeof(RGBQUAD)*16 

        其中,16是因為4位圖像代表2的四次方,所以是16,再乘sizeof(RGBQUAD)

             54是信息頭的biSize+文件頭的bfType

           1和3是長和寬  


    bfReserved1和bfReserved1必須為0

 

  (2)BITMAPV4HEADER

typedef struct
{
DWORD bV4Size ; // size of the structure = 120
LONG bV4Width ; // width of the image in pixels
LONG bV4Height ; // height of the image in pixels
WORD bV4Planes ; // = 1
WORD bV4BitCount ; // bits per pixel (1, 4, 8, 16, 24, or
32)
DWORD bV4Compression ; // compression code
DWORD bV4SizeImage ; // number of bytes in image
LONG bV4XPelsPerMeter ; // horizontal resolution
LONG bV4YPelsPerMeter ; // vertical resolution
DWORD bV4ClrUsed ; // number of colors used
DWORD bV4ClrImportant ; // number of important colors
DWORD bV4RedMask ; // Red color mask
DWORD bV4GreenMask ; // Green color mask
DWORD bV4BlueMask ; // Blue color mask
DWORD bV4AlphaMask ; // Alpha mask
DWORD bV4CSType ; // color space type
CIEXYZTRIPLE bV4Endpoints ; // XYZ values
DWORD bV4GammaRed ; // Red gamma value
DWORD bV4GammaGreen ; // Green gamma value
DWORD bV4GammaBlue ; // Blue gamma value
}
BITMAPV4HEADER, * PBITMAPV4HEADER ;

  (3)BITMAPV5HEADER

typedef struct
{
DWORD bV5Size ; // size of the structure = 120
LONG bV5Width ; // width of the image in pixels
LONG bV5Height ; // height of the image in pixels
WORD bV5Planes ; // = 1
WORD bV5BitCount ; // bits per pixel (1,4,8,16,24,or32)
DWORD bV5Compression ; // compression code
DWORD bV5SizeImage ; // number of bytes in image
LONG bV5XPelsPerMeter ; // horizontal resolution
LONG bV5YPelsPerMeter ; // vertical resolution
DWORD bV5ClrUsed ; // number of colors used
DWORD bV5ClrImportant ; // number of important colors
DWORD bV5RedMask ; // Red color mask
DWORD bV5GreenMask ; // Green color mask
DWORD bV5BlueMask ; // Blue color mask
DWORD bV5AlphaMask ; // Alpha mask
DWORD bV5CSType ; // color space type
CIEXYZTRIPLE bV5Endpoints ; // XYZ values
DWORD bV5GammaRed ; // Red gamma value
DWORD bV5GammaGreen ; // Green gamma value
DWORD bV5GammaBlue ; // Blue gamma value
DWORD bV5Intent ; // rendering intent
DWORD bV5ProfileData ; // profile data or filename
DWORD bV5ProfileSize ; // size of embedded data or filename
DWORD bV5Reserved ;
}
BITMAPV5HEADER, * PBITMAPV5HEADER ;

   可能有些讀者看到這里有點亂,下面我來說一下這三者的關系:

    BITMAPFILEHEADER是最早的版本

    V4HEADER是windows95的拓展:

      Windows 95 更改了一些原始 BITMAPINFOHEADER 欄位的定義。前 11 個欄位與 BITMAPINFOHEADER 結構中的相同,後 5 個欄位支援Windows 95 和 Windows NT 4.0 的圖像顏色調配技術。除非使用 BITMAPV4HEADER結構的後四個欄位,否則您應該使用 BITMAPINFOHEADER(或 BITMAPV5HEADER)

      這也就意味着:v4相當於是BITMAPFILEHEADER的一種拓展結構,如果不用那些拓展因素的話,是可以通用的,也就是可以用v4來接收FILEHEADER的數據,FILEHEADER相當於v4的子集

    V5HEADER是windows98和2000的拓展:

      基本原理同v4,有四個新欄位,只有其中三個有用。這些欄位支援 ICC Profile Format Specification

      特別的是:BITMAPV5HEADER 的 bV5CSType 欄 位 能 擁 有 幾 個 不 同 的 值 。

          如 果 是LCS_CALIBRATED_RGB,那么它就與 BITMAPV4HEADER 結構相容。bV5Endpoints 欄位和伽馬欄位必須有效。

          如果 bV5CSType 欄位是 LCS_sRGB,就不用設定剩余的欄位。

          如果 bV5CSType 欄位是 PROFILE_EMBEDDED,則 DIB 檔案包含一個 ICC 設定檔案。

          如果欄位是 PROFILE_LINKED,DIB 檔案就包含了 ICC 設定檔案的完整路徑和檔案名稱。

      在上面最后兩種情況下,bV5ProfileData 都是從 BITMAPV5HEADER 開始到設定檔案資料或檔案名稱起始位置的偏移量。bV5ProfileSize 欄位給出了資
料或檔案名的大小。不必設定 bV5Endpoints 和伽馬欄位。

(三)位圖信息頭

  位圖信息頭(bitmap-information header)包含了位圖信息頭的大小、圖像的寬高、圖像的色深、壓縮說明圖像數據的大小和其他一些參數。

  (1)BITMAPINFOHEADER

typedef struct tagBITMAPINFOHEADER // bmih
{
DWORD biSize ; // size of the structure = 40
LONG biWidth ; // width of the image in pixels
LONG biHeight ; // height of the image in pixels
WORD biPlanes ; // = 1
WORD biBitCount ; // bits per pixel (1, 4, 8, 16, 24, or 32)
DWORD biCompression ; // compression code
DWORD biSizeImage ; // number of bytes in image
LONG biXPelsPerMeter ; // horizontal resolution
LONG biYPelsPerMeter ; // vertical resolution
DWORD biClrUsed ; // number of colors used
DWORD biClrImportant ; // number of important colors
}
BITMAPINFOHEADER, * PBITMAPINFOHEADER ;

  biSize       本結構的大小,根據不同的操作系統而不同,在Windows中,此字段的值總為28h字節=40字節

  biWidth      BMP圖像的寬度,單位像素  

  biHeight      總為0

  biPlanes      總為0

  biBitCount      BMP圖像的色深,即一個像素用多少位表示,常見有

            1、4、8、16、24和32,分別對應單色、16色、256色、16位高彩色、24位真彩色和32位增強型真彩色

  biCompression   壓縮方式,0表示不壓縮,1表示RLE8壓縮,2表示RLE4壓縮,3表示每個像素值由指定的掩碼決定

  biSizeImage    BMP圖像數據大小,必須是4的倍數,圖像數據大小不是4的倍數時用0填充補足

  biXPelsPerMeter  水平分辨率,單位像素/m

  biYPelsPerMeter  垂直分辨率,單位像素/m

  biClrUsed     BMP圖像使用的顏色,0表示使用全部顏色,對於256色位圖來說,此值為100h=256

  biClrImportant  重要的顏色數,此值為0時所有顏色都重要,對於使用調色板的BMP圖像來說,當顯卡不能夠顯示所有顏色時,此值將輔助驅動程序顯示顏色

 

(2)BITMAPCOREHEADER

    “在 OS/2 樣式的 DIB 內, BITMAPFILEHEADER 結構後緊跟了 BITMAPCOREHEADER結構”  也就意味着,在Windows中,BITMAPFILEHEADER后面,跟的是上面的 BITMAPINFOHEADER 而不是這個 BITMAPCOREHEADER,現在基本不怎么用這個了

 

typedef struct tagBITMAPCOREHEADER // bmch
{
DWORD bcSize ; // size of the structure = 12
WORD bcWidth ; // width of image in pixels
WORD bcHeight ; // height of image in pixels
WORD bcPlanes ; // = 1
WORD bcBitCount ; // bits per pixel (1, 4, 8, or 24)
}
BITMAPCOREHEADER, * PBITMAPCOREHEADER ;

 

BITMAPCOREHEADER 和 BITMAPINFOHEADER 兩者的區別:

      bcSize的值不同,BITMAPCOREHEADER中為40,而BITMAPCOREHEADER 中為12

 (四)顏色表

  彩色表/調色板(color table)是單色、16色和256色圖像文件所特有的,相對應的調色板大小是2、16和256,調色板以4字節為單位,每4個字節存放一個顏色值,圖像 的數據是指向調色板的索引。

  而24色為真彩色,是沒有調色盤,顏色數據直接存在位圖數據中

  以16色舉例:將調色板想象成一個數組,每個數組元素的大小為4字節。

typedef struct
{
    unsigned char rgbBlue; //該顏色的藍色分量  
    unsigned char rgbGreen; //該顏色的綠色分量  
    unsigned char rgbRed; //該顏色的紅色分量  
    unsigned char rgbReserved; //保留值  
} RgbQuad;

假設有一16色的BMP圖像的調色板數據為:

調色板[0]=黑...調色板[9]=紅、調色板[10]=綠…調色板[16]=白

對應代碼:

RgbQuad color_table[16] = {// Blue Green Red Unused
            { 8, 8, 8, 0 },  //0
            { 4, 100, 200, 0 },  //1
            { 112, 128, 0, 0 },  //2
            { 120, 120, 120, 0 },  //3
            { 180, 160, 20, 0 },  //4
            { 200, 176, 152, 0 },  //5
            { 204, 204, 204, 0 },  //6
            { 200, 192, 192, 0 },  //7
            { 112, 112, 112, 0 },  //8
            { 0, 0, 252, 0 },  //9
            { 0, 248, 0, 0 },  //10
            { 0, 248, 248, 0 },  //11
            { 248, 0, 0, 0 },  //12
            { 248, 0, 248, 0 },  //13
            { 248, 248, 0, 0 },  //14
            { 248, 248, 248, 0 } }; //15

 (五)文件數據(data)

  如果圖像是單色、16色和256色,則緊跟着調色板的是位圖數據,位圖數據是指向調色板的索引序號。

  如果位圖是16位、24位和32位色,則圖像文件中不保留調色板,即不存在調色板,圖像的顏色直接在位圖數據中給出。

  16位圖像使用2字節保存顏色值,常見有兩種格式:5位紅5位綠5位藍和5位紅6位綠5位藍,即555格式和565格式。555格式只使用了15 位,最后一位保留,設為0。

  24位圖像使用3字節保存顏色值,每一個字節代表一種顏色,按紅、綠、藍排列。

  32位圖像使用4字節保存顏色值,每一個字節代表一種顏色,除了原來的紅、綠、藍,還有Alpha通道,即透明色。

  如果圖像帶有調色板,則位圖數據可以根據需要選擇壓縮與不壓縮,如果選擇壓縮,則根據BMP圖像是16色或256色,采用RLE4或RLE8壓縮算 法壓縮。

  例:     

     這樣一個1*5的五個紅色像素的圖片,對應的二進制代碼為:

       我們可以理解為:color_table[9]的值為紅色,因此紅色是16色調色盤的第10個元素,而0是第一個元素,對應的顏色表為白色,那么1*5為什么不是5個元素而是八個呢?這個我查了資料應該是:每行象素數是4的倍數,但是這里不知為何是8的倍數,默認補了3個空(如果有人清楚麻煩留言說下。。)。

     要注意的是,顏色的二進制代碼對應規則是:從左下角第一個元素開始對應的,自左向右,右側沒有像素時上移一行,再次左起

  再比如:     和  

      兩者對應的二進制代碼均為:

      那么,為何會這樣呢?因為按上面的對應規則,兩張圖均是先讀取8像素的紅色數據,然后第一張圖換到上一行讀取最左側藍色,而第二張圖繼續讀取8位藍色,因此二者代碼一樣

     如何區分呢?這就用到了上面的位圖信息頭,里面包含了圖片的寬高,在繪制圖片的時候如果寬度到頭了,便會自動換行,繪制上一行。

 (六)源代碼

#include"stdafx.h"

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include <iostream>
using namespace std;

#define real_width 240
#define real_height 320
//#include "windef.h"        //
typedef unsigned char BYTE;
typedef unsigned long DWORD;    
typedef unsigned short WORD;

#pragma pack(2)
typedef struct {
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} BMPFILEHEADER_T;
struct BMPFILEHEADER_S{
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
};
typedef struct{
    DWORD      biSize;
    long       biWidth;
    long       biHeight;
    WORD       biPlanes;
    WORD       biBitCount;
    DWORD      biCompression;
    DWORD      biSizeImage;
    long       biXPelsPerMeter;
    long       biYPelsPerMeter;
    DWORD      biClrUsed;
    DWORD      biClrImportant;
} BMPINFOHEADER_T;

typedef struct
{
    unsigned char rgbBlue; //該顏色的藍色分量  
    unsigned char rgbGreen; //該顏色的綠色分量  
    unsigned char rgbRed; //該顏色的紅色分量  
    unsigned char rgbReserved; //保留值  
} RgbQuad;
#pragma pack()
void Create24(BYTE * pData, int width, int height, char * filename)
{
    int size = width*height * 3; // 每個像素點3個字節
    // 位圖第一部分,文件信息
    BMPFILEHEADER_T bfh;
    bfh.bfType = 0x4d42;//BM
    bfh.bfSize = size  // data size
        + sizeof(BMPFILEHEADER_T) // first section size
        + sizeof(BMPINFOHEADER_T) // second section size
        ;
    bfh.bfReserved1 = 0; // reserved 
    bfh.bfReserved2 = 0; // reserved
    bfh.bfOffBits = bfh.bfSize - size;

    // 位圖第二部分,數據信息
    BMPINFOHEADER_T bih;
    bih.biSize = sizeof(BMPINFOHEADER_T);
    bih.biWidth = width;
    bih.biHeight = height;
    bih.biPlanes = 1;
    bih.biBitCount = 24;
    bih.biCompression = 0;
    bih.biSizeImage = size;
    bih.biXPelsPerMeter = 0;
    bih.biYPelsPerMeter = 0;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;
    FILE * fp;
    fopen_s(&fp,filename, "wb");
    if (!fp) return;
    fwrite(&bfh, 1, sizeof(BMPFILEHEADER_T), fp);
    fwrite(&bih, 1, sizeof(BMPINFOHEADER_T), fp);
    fwrite(pData, 1, size, fp);
    fclose(fp);
}

void Create4(BYTE * pData, int width, int height, char * filename)
{
    int size = width*height ; // 每個像素點2個字節
    // 位圖第一部分,文件信息
    BMPFILEHEADER_T bfh;
    bfh.bfType = 0x4d42;//BM
    bfh.bfSize = sizeof(RgbQuad)*16  //一共16種顏色,一個顏色4字節 
        + sizeof(BMPFILEHEADER_T) // first section size
        + sizeof(BMPINFOHEADER_T) // second section size
        +size;
    bfh.bfReserved1 = 0; // reserved 
    bfh.bfReserved2 = 0; // reserved
    bfh.bfOffBits = bfh.bfSize - size;

    // 位圖第二部分,數據信息
    BMPINFOHEADER_T bih;
    bih.biSize = sizeof(BMPINFOHEADER_T);
    bih.biWidth = width;
    bih.biHeight = height;
    bih.biPlanes = 1;
    bih.biBitCount = 4;
    bih.biCompression = 0;
    bih.biSizeImage = size;
    bih.biXPelsPerMeter = 0;
    bih.biYPelsPerMeter = 0;
    bih.biClrUsed = 0;
    bih.biClrImportant = 0;

    //位圖第三部分,調色盤
    RgbQuad color_table[16] = {// Blue Green Red Unused
            { 8, 8, 8, 0 },  //0
            { 4, 100, 200, 0 },  //1
            { 112, 128, 0, 0 },  //2
            { 120, 120, 120, 0 },  //3
            { 180, 160, 20, 0 },  //4
            { 200, 176, 152, 0 },  //5
            { 204, 204, 204, 0 },  //6
            { 200, 192, 192, 0 },  //7
            { 112, 112, 112, 0 },  //8
            { 0, 0, 252, 0 },  //9
            { 0, 248, 0, 0 },  //10
            { 0, 248, 248, 0 },  //11
            { 248, 0, 0, 0 },  //12
            { 248, 0, 248, 0 },  //13
            { 248, 248, 0, 0 },  //14
            { 248, 248, 248, 0 } }; //15

    FILE * fp;
    fopen_s(&fp, filename, "wb");
    if (!fp) return;
    fwrite(&bfh, 1, sizeof(BMPFILEHEADER_T), fp);
    fwrite(&bih, 1, sizeof(BMPINFOHEADER_T), fp);
    fwrite(&color_table, 1, sizeof(RgbQuad) * 16, fp);
    fwrite(pData, 1, size, fp);
    fclose(fp);
}

void main()
{

    int i = 0, j = 0;
    int chose ;
    cout << "請輸入新位圖的位深(目前能輸入4或24)" << endl;
    cin >> chose;
    switch (chose)
    {
    case 24:
        struct {
            BYTE b;
            BYTE g;
            BYTE r;
        } pRGB[240][320];  // 定義位圖數據
        memset(pRGB, 255, sizeof(pRGB)); // 設置背景為黑色
        // 在中間畫一個100*100的矩形
        for (i = 70; i<170; i++){
            for (j = 110; j<210; j++){
                pRGB[i][j].g = 255;
                pRGB[i][j].r = 255;
                pRGB[i][j].b = 0;
            }
        }
        // 生成BMP圖片
        Create24((BYTE*)pRGB, real_height, real_width, "f:\\bmp-24.bmp");
        i = 0, j = 0;
        cout << "創建24-bit成功" << endl;
        break;
    case 4:
        BYTE data[real_height][real_width];
        memset(data, 255,sizeof(data));
        for (i = 30; i<130; i++){
            for (j = 20; j<100; j++){
                data[i][j] = 0x9;
            }
        }
        Create4((BYTE*)data, real_width, real_height, "f:\\bmp-4.bmp");
        cout << "創建4-bit成功" << endl;
        break;
    default:
        break;
    }
    
}

  注意事項:

    (1)對齊格式:開頭結構體前后要加上#pragma pack(2)和#pragma pack(),作用是取消自動對齊,否則結構體大小不一樣。

    (2)使用方式:在控制台輸入4或24進行選擇,分別在f盤生成16色或24色的圖片


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM