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色的图片

转载于:https://www.cnblogs.com/Asssjc/p/5742319.html

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值