博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言 控制台程序生成BMP图片
阅读量:5902 次
发布时间:2019-06-19

本文共 13378 字,大约阅读时间需要 44 分钟。

首先,本篇文章参考了众多博客,有:

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 structureRGBQUAD 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 = 12WORD bcWidth ; // width of image in pixelsWORD bcHeight ; // height of image in pixelsWORD bcPlanes ; // = 1WORD 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 = 120LONG bV4Width ; // width of the image in pixelsLONG bV4Height ; // height of the image in pixelsWORD bV4Planes ; // = 1WORD bV4BitCount ; // bits per pixel (1, 4, 8, 16, 24, or32)DWORD bV4Compression ; // compression codeDWORD bV4SizeImage ; // number of bytes in imageLONG bV4XPelsPerMeter ; // horizontal resolutionLONG bV4YPelsPerMeter ; // vertical resolutionDWORD bV4ClrUsed ; // number of colors usedDWORD bV4ClrImportant ; // number of important colorsDWORD bV4RedMask ; // Red color maskDWORD bV4GreenMask ; // Green color maskDWORD bV4BlueMask ; // Blue color maskDWORD bV4AlphaMask ; // Alpha maskDWORD bV4CSType ; // color space typeCIEXYZTRIPLE bV4Endpoints ; // XYZ valuesDWORD bV4GammaRed ; // Red gamma valueDWORD bV4GammaGreen ; // Green gamma valueDWORD bV4GammaBlue ; // Blue gamma value}BITMAPV4HEADER, * PBITMAPV4HEADER ;

  (3)BITMAPV5HEADER

typedef struct{DWORD bV5Size ; // size of the structure = 120LONG bV5Width ; // width of the image in pixelsLONG bV5Height ; // height of the image in pixelsWORD bV5Planes ; // = 1WORD bV5BitCount ; // bits per pixel (1,4,8,16,24,or32)DWORD bV5Compression ; // compression codeDWORD bV5SizeImage ; // number of bytes in imageLONG bV5XPelsPerMeter ; // horizontal resolutionLONG bV5YPelsPerMeter ; // vertical resolutionDWORD bV5ClrUsed ; // number of colors usedDWORD bV5ClrImportant ; // number of important colorsDWORD bV5RedMask ; // Red color maskDWORD bV5GreenMask ; // Green color maskDWORD bV5BlueMask ; // Blue color maskDWORD bV5AlphaMask ; // Alpha maskDWORD bV5CSType ; // color space typeCIEXYZTRIPLE bV5Endpoints ; // XYZ valuesDWORD bV5GammaRed ; // Red gamma valueDWORD bV5GammaGreen ; // Green gamma valueDWORD bV5GammaBlue ; // Blue gamma valueDWORD bV5Intent ; // rendering intentDWORD bV5ProfileData ; // profile data or filenameDWORD bV5ProfileSize ; // size of embedded data or filenameDWORD 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 = 40LONG biWidth ; // width of the image in pixelsLONG biHeight ; // height of the image in pixelsWORD biPlanes ; // = 1WORD biBitCount ; // bits per pixel (1, 4, 8, 16, 24, or 32)DWORD biCompression ; // compression codeDWORD biSizeImage ; // number of bytes in imageLONG biXPelsPerMeter ; // horizontal resolutionLONG biYPelsPerMeter ; // vertical resolutionDWORD biClrUsed ; // number of colors usedDWORD 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 = 12WORD bcWidth ; // width of image in pixelsWORD bcHeight ; // height of image in pixelsWORD bcPlanes ; // = 1WORD 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 
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

你可能感兴趣的文章
iOS开发UI篇—使用picker View控件完成一个简单的选餐应用
查看>>
Apple Developer Registration and DUNS Number Not Accepted
查看>>
Hadoop学习笔记系列文章导航
查看>>
SpringMVC中ModelAndView addObject()设置的值jsp取不到的问题
查看>>
Prometheus : 入门
查看>>
使用 PowerShell 创建和修改 ExpressRoute 线路
查看>>
PHP如何学习?
查看>>
在C#中获取如PHP函数time()一样的时间戳
查看>>
Redis List数据类型
查看>>
大数据项目实践(四)——之Hive配置
查看>>
初学vue2.0-组件-文档理解笔记v1.0
查看>>
NG-ZORRO-MOBILE 0.11.9 发布,基于 Angular 7 的 UI 组件
查看>>
我就是一个救火员(DBA救援)
查看>>
Centos7安装Gitlab10.0
查看>>
Windows Server 笔记(六):Active Directory域服务:域控制器安装
查看>>
discuz X3登录流程分析
查看>>
上传图片预览
查看>>
程序设计的一些原理
查看>>
lagp,lacp详解
查看>>
LVS之DR模式原理与实践
查看>>