DIB位图
1. DIB文件格式
可以将DIB认为就是一种文件格式,DIB文件的扩展名是BMP,在极个别的情况下也可以是DIB。
程序可以把DIB文件除去开始的14个字节外,整个载入到一块连续的内存去榆中,这有时被称为“紧凑DIB格式的位图”。WIndows下的应用程序可以用这种格式创建画刷或者通过Windows剪切板来交换图像。程序还拥有对该DIB内容的完全控制,可以对此DIB进行任意修改。
程序也可以在内存中创建自己的DIB,然后把他们存到文件中。这些DIB中的图像可以工GDI函数绘制。程序也可以直接对像素操作,在此过程中可以使用其他基于内存的DIB。
2. OS/2风格的DIB
最早在OS/2 1.1版本中引入的位图文件相兼容的Windows DIB文件格式,有四个主要部分:
◇文件头
◇信息头
◇RGB颜色表(有时可能没有)
◇位图的像素位
前两部分可以看做C数据结构,第三部分看做一个数据结构的数组。在内存中紧凑格式的DIB有三部分:
◇信息头
◇RGB颜色表(有时可能没有)
◇位图的像素位
他和存在于文件中的DIB一模一样,唯一的区别是没有文件头。DIB文件的文件头定义如下:
typedef struct tabBITMAPFILEHEDER //bmfh,bmfh是书中推荐的缩写
{
WORD bfType;//文件签名 ,值就是“BM”,十六进制表示为 0x4D42
DWORD bfSize;//整个文件的长度
WORD bfReserved1;//必须是0
WORD bfReserved2;//必须是0
DWORD bfOffsetBits;//到位图像素位的位移
}
BITMAPFILEHEADER,*PBITMAPFILEHEADER;
DIB文件的信息头定义如下:
typedef tagBITMAPCOREHEADER //bmch
{
DWORD bcSize;//结构大小= 12;
WORD bcWidth;//以像素计的图像的宽度
WORD bcHeight;//以像素计的图像的高度
WORD bcPlanes;// = 1;
WORD bcBitCount;//每个像素的位数(1,4,8, or 24)
}BITMAPCOREHEADER,*PBITMAPCOREHEADER;
DIB中的颜色数等于2^bmch.bcBitCount,因此bcBitCount字段就等于,1对应于双色DIB,4对应于16色DIB,8对应于256色DIB。在提到“一个8位DIB”时,指的是一个DIB的每个像素有8位。
对于前三种情形(位数为1、4和8),BITMAPCOREHEADER后面是一个颜色表。24位DIB没有颜色表,颜色表是一个数组,数组中的元素是一个3字节长的RBGTRIPLE结构,对应于图像中的每一种颜色:
typedef struct tagRGBTRIPLE
{
BYTE rgbBlue;//蓝色值
BYTE rgbGreen;//绿色值
BYTE rgbRed;//红色值
}RGBTRIPLE,*PRGBTRIPLE;
在WINGDI.h中文件也定义了下面的结构:
typedef tagBITMAPCOREINFO//bmci
{
BITMAPCOREHEADER bmciHeader;//基本信息头
RGBTRIPLE bmciColors[1];//颜色表数组
}BITMAPCOREINFO,*PBITMAPCOREINFO;
这个结构把信息头和颜色表结合在一起。尽管在这里颜色表数组看起来只有1,但是实际上在DIB文件中总会看到不止一个RGBTRIPLE结构。根据每一像素的位数,比如位数是8,那么颜色就有2^8=256,那么就有256个RGBTRIPLE结构。如果需要为一个8位DIB的PBITMAPCOREINFO结构分配内存,可以像下面这样:
pbmci=malloc(sizeof (BITMAPCOREINFO)+255*sizeof(RGBTRIPLE));
然后可以像下面这样对任何一个RGBTRIPLE结构进行访问:
pbmci->bmciColors[i];
3.自下而上存储
像大多数位图格式一样,DIB中的像素位是以水平的行来排列的,图像中的行数等于BITMAPCOREHEADER结构中的bcHeight字段的值。但是和其他大多数位图格式不同的是,DIB是从图像的最下面一行开始,然后逐渐向上来存储整个图像的。
4.Windows扩展DIB
这个格式的 DIB 和前面提到的格式一样,也是以一个 BITMAPFILEHEADER 结构开头,但是,接在后面的是一个 BITMAPINFOHEADER 结构,而不是 BITMAPCOREHEADER 结构:typedef struct tagBITMAPINFOHEADER {
DWORD biSize; // 结构体大小 = 40
LONG biWidth; // 以像素计的图像的宽度
LONG biHeight; // 以像素计的图像的高度
WORD biPlanes; // = 1
WORD biBitCount; // 每个像素的位数(1,4,8,16,24,32) 自己测试发现 只有16位以上的才是彩色的。
DWORD biCompression; // 压缩编码 通常为 BI_RGB
DWORD biSizeImage; // 图像的字节数
LONG biXPelsPerMeter; // 水平分辨率,通常为 0
LONG biYPelsPerMeter; // 垂直分辨率,通常为 0
DWORD biClrUsed; // 用到的颜色数,(1,4,8 的情况下)0 或 2^biClrUsed 都为最大值。
DWORD biClrImportant; // 重要颜色的数目,通常为 0 或 biClrUsed 的值
} BITMAPINFOHEADER, *PBITMAPINFOHEADER;
你可以从这个结构的第一个字段来区别 OS/2 兼容的 DIB 和 Windows DIB: 前者是 12,后者是 40。
注:如果biHeight是负数,那么这个DIB就是从上到下的,原点为左上角。
颜色表不再是一个 RGBTRIPLE 结构的数组。接在 BITMAPINFOHEADER 结构后面的是一个 RGBQUAD 结构的数组:
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved; // = 0;
} RGBQUAD;
typedef struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];
}BITMAPINFO;
在8位DIB中,如果biClrUsed是0或256,那么颜色表有256个元素.当一个8位DIB的颜色表中的元素全部是灰度(也就是说,红、绿、蓝的级别一样)而且灰度的的等级是均匀的升高,那么像素的值就代表了灰度的比例。例如biClrUsed是256,那么一个值为0的像素是黑色,一个值为128的像素为50%灰色,值为255的像素值为白色。
5.DIB像素位
DIB文件的最后部分是由真正的像素位组成的。像素位按水平排列,从图像的最下一行开始逐渐向上包括整个图像。DIB中的行数等于BITMAPCOREHEADER结构中的bcHeight字段的值。每一行的编码的像素数等于bcWidth字段。每一行的像素是从左向右安排的。每一个像素的位数由bcBitCount字段定义,可以是1、4/8或者24。
每一行所用的字节数总是4的倍数,“Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充”因为我们便是整数大小用的是int(long也是),int占用4个字节,这是32位系统定死的,所以必须是4的整数倍,也就是四个字节造成的!可以用一下方法计算出来:
RowLength=4*((bmch.bcWidth*bmch.bcBitCount+31)/32);
6.显示DIB
Windows支持两个函数:SetDibitsToDevice和StretchDiBits。每个函数都使用存储在内存中的DIB,并且可以显示整个DIB或者DIB 的部分矩形区域,前者直接显示,后者可以拉升或缩小DIB的行数和列数。
对于SetDibitsToDevice和StretchDiBits函数,你需要包括一个指向DIB的BITMAPINFO结构的指针,以及像素位的指针:
除了这里两个指针,还需要知道DIB有多少像素宽,多少像素高。如果只显示DIB的一部分,就不需要知道他们的具体值。SetDibitsToDevice函数如下:
你可以显示整个DIB图像,也可以只显示一部分。cxSrc,cySrc,xSrc和ySrc就是作用于只显示一部分DIB图像的。这几个参数的使用可能会造成DIB像素数据的上下倒置。暂时记住,要显示整个DIB,则应把xSrc和ySrc设成0,cxSrc和cySrc分别等于DIB的像素宽度和高度。注意,对于自下而上的DIB,BITMAPINFOHEADER结构的biHeight的字段是负数,所以cySrc应该设置成biHeight字段的绝对值。这四个参数不是逻辑单位,而是像素坐标点和像素尺寸。无论映射模式是什么,在输出设备上显示的DIB总是cxSrc个像素宽,cySrc个像素高。
SetDibitsToDevice函数通常有4个参数设成0:
通过缩小或拉升一个DIB,可以在输出设备上按特定大小显示它,为此你可以使用StretchDIBits:
该函数的参数和 SetDibitsToDevice一样,但有三点例外
◆目标坐标包含逻辑宽度(cxDst)、逻辑高度(cyDst)和起始点。
◆此函数并不能通过顺序显示DIB来减少内存需求
◆最后一个操作是光栅操作。一般设置为SRCCOPY,不更改像素值显示
7.DIB转DDB
但在显示DIB时Windows需要的时间很长,解决方式是吧DIB转换成DDB
我们其实已经知道怎么做了:如果你有一个DIB,便可以用CreateCompatibleBitmap来创建一个和显示硬件兼容的同样大小的GDI位图对象。然后可以把这个位图对象选入一个内存设备环境中,并调用SetDibitsToDevice函数在这个内存设备环境上绘图。其结果就是一个DDB,拥有和DIB同样的图像,但是其颜色组织和你的显示硬件兼容。
或者也可以用CreateDIBitmap分几步完成同样的事情。但是如果你的DIB只显示一次那么直接使用SetDibitsToDevice函数就可以,因此转换成DDB以后再显示和直接使用SetDibitsToDevice显示的速度一样。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构