Windows 位图
目录
2.1.1 CreateCompatibleBitmap 3
第1章简介
下图显示了三种位图:DFB、DDB、DIB。
图1.1
1.1 DFB
DFB(Device-format bitmap)是设备格式位图。假如设备是显示器,那么DFB就在显卡内存中。DFB格式并没有统一的标准,所以直接访问DFB并不是一个明智的做法。事实上,在Windows系统里一般是无法直接访问DFB的,程序员常常使用设备的HDC与之联系,如下面的三行代码将在屏幕上绘制一个矩形,其实质就是通过屏幕的HDC访问DFB,往显存里绘制了一个矩形。
HDC hDC = GetDC(NULL); //获得屏幕的 HDC
Rectangle(hDC,200,100,50,90); //绘制一个矩形
ReleaseDC(NULL,hDC); //释放 HDC
1.2 DDB
DDB(Device-dependent bitmap)是设备相关位图,其实就是HBITMAP。笔者认为它存在的理由之一就是:通过HDC访问DFB的效率有时太低了。它叫设备相关位图是因为它的内部格式与DFB是有关系的,保证了与DFB相互转换、传输时的高效率。
访问DDB的方法有三种:
1、通过内存HDC;
2、分配内存,通过GetDIBits获取位图数据,通过SetDIBits将修改后的位图数据写入HBITMAP;
3、创建DDB时获取位图数据的首地址,这样就可以直接访问位图了。不过这种方法创建的位图实质上已经不是DDB了,它不再与DFB有关联,相互转换时不能保证最高的效率。
DDB与DFB可通过BitBlt等函数相互转换、传输。
1.3 DIB
DDB的格式随DFB的格式而变,如果需要根据不同的DDB格式编写不同的代码去操纵一个像素,那将是程序员的恶梦。这个时候就需要DIB了。
DIB(Device-independent bitmap)是设备无关位图,是程序员访问DDB时的位图标准格式。知道了这个格式就可以对位图中的单个像素进行高效的操作了。DIB位图读写完毕后,可通过API函数将其转换为DDB。
DIB的实质就是一种位图格式,而且它基本上就是.bmp文件格式。所以,把DIB理解为加载进内存中的.bmp文件也是可以的。
DIB通过GetDIBits、SetDIBits访问DDB。
DIB通过SetDIBitsToDevice等函数可直接访问DFB。因为格式不同,所以需要大量的转换,不能保证高效性。
第2章相关API
2.1 创建
创建位图的API有5个:
CreateBitmap
CreateBitmapIndirect
CreateCompatibleBitmap
CreateDIBitmap
CreateDIBSection
下面逐个介绍
2.1.1 CreateCompatibleBitmap
使用示例:
HDC hDC = GetDC(NULL);
HBITMAP hBmp = CreateCompatibleBitmap(hDC,100,50);
第一行代码获取屏幕的 hDC;
第二行代码将创建与屏幕兼容的DDB位图,其大小为100×50。
hBmp与hDC兼容的含义就是其格式尽量与hDC的DFB保持一致,这样在与DFB相互转换、传输时效率就会比较高。
2.1.2 CreateBitmap
此函数的声明如下:
HBITMAP CreateBitmap(int nWidth, //位图宽,单位:像素
int nHeight, //位图高,单位:像素
UINT cPlanes, //位图颜色面数
UINT cBitsPerPel, //一个像素的比特数
CONST VOID *lpvBits); //对像素进行初始化
它的实质就是创建了一个三维数组。数组大小为:nWidth×nHeight×cPlanes,数组中的每个元素代表一个像素,每个像素占用的比特数为cBitsPerPel。
如果要对三维数组进行初始化,请指定参数lpvBits。
现在再来看看上一节的代码:
HBITMAP hBmp = CreateCompatibleBitmap(hDC,100,50);
它可以被CreateBitmap函数代替,具体代码如下:
HBITMAP hBmp = CreateBitmap(100,50
,GetDeviceCaps(hDC,PLANES)
,GetDeviceCaps(hDC,BITSPIXEL)
,NULL);
2.1.3 CreateBitmapIndirect
CreateBitmapIndirect是CreateBitmap的简化版本,如下面的代码:
BITMAP bm;
CreateBitmapIndirect(&bm);
等价于下面的代码:
CreateBitmap(bm.bmWidth,bm.bmHeight
,bm.bmPlanes,bm.bmBitsPixel,bm.bmBits);
2.1.4 CreateDIBitmap
CreateDIBitmap不是创建一个DIB位图,而是创建一个DDB位图,然后用DIB位图数据对这个DDB位图进行初始化。其声明如下:
HBITMAP CreateDIBitmap(HDC hdc,
CONST BITMAPINFOHEADER *lpbmih, //指定位图的宽和高
DWORD fdwInit, //是否初始化
CONST VOID *lpbInit, //DIB像素数据首地址
CONST BITMAPINFO *lpbmi, //DIB信息及颜色表)
UINT fuUsage); //DIB_RGB_COLORS
它等效于如下代码,即首先创建DDB位图,然后再根据DIB位图数据对其进行初始化。
HBITMAP hBmp = CreateCompatibleBitmap(hDC
,lpbmi->biWidth,labs(lpbmi->biHeight));
if(CBM_INIT==fdwInit)
{
SetDIBits(hDC,hBmp,0,labs(lpbmi->biHeight),lpbInit,lpbmi,fuUsage);
}
2.1.5 CreateDIBSection
以上创建的位图都是DDB位图,CreateDIBSection创建的将是一个DIB位图,其声明如下:
HBITMAP CreateDIBSection(
HDC hdc, // handle to device context
CONST BITMAPINFO *pbmi,
// pointer to structure containing bitmap size,
// format, and color data
UINT iUsage, // color data type indicator: RGB values or
// palette indexes
VOID **ppvBits, // pointer to variable to receive a pointer to
// the bitmap's bit values
HANDLE hSection, // optional handle to a file mapping object
DWORD dwOffset // offset to the bitmap bit values within the
// file mapping object
);
第1个参数hdc一般没什么用,仅当iUsage为DIB_PAL_COLORS时才会用到它;
第2个参数pbmi指定了DIB位图的宽度、高度、颜色位及颜色表;
第3个参数iUsage说明了颜色表中的颜色含义。iUsage为DIB_RGB_COLORS时,颜色表中的颜色是RGB格式;iUsage为DIB_PAL_COLORS时,颜色表中的颜色是索引值,这个时候就需要用第1个参数hdc将索引值转换为RGB值;
第4个参数ppvBits是一个输出参数,*ppvBits是一个void*,它指向了DIB像素数据的首地址。也就是说CreateDIBSection会为DIB位图分配内存,然后把像素数据首地址通过ppvBits传出来,程序员根据此地址即可对DIB位图进行操作。
最后两个参数一般不用,直接设置为NULL和0即可。
2.1.6 小结
CreateCompatibleBitmap是最简单且经常用到的;
CreateBitmap是最正规的;
CreateBitmapIndirect是CreateBitmap的简化版;
CreateDIBitmap在创建DDB位图后,用DIB位图进行初始化;
CreateDIBSection最特殊,它创建的不是DDB,而是DIB。也只有它能获得位图数据的地址,能够对位图数据进行直接操作。
2.2 查询
给定一个HBITMAP hBmp,如何得知它的宽度、高度等信息?答案就是使用GetObject函数,其示例代码如下:
BITMAP bm;
GetObject(hBmp,sizeof(bm),&bm);
此时:
bm.bmWidth; //位图宽度,单位:像素
bm.bmHeight; //位图高度,单位:像素。如果是DIB,它可能为负
bm.bmWidthBytes; //位图一行的字节数
bm.bmPlanes; //位图颜色面数
bm.bmBitsPixel; //每个像素的比特数
bm.bmBits; //如果是DIB,它就是位图数据的地址,否则为NULL
只有通过CreateDIBSection获得的HBITMAP,它对应的bm.bmBits才不为NULL。此时,下面的代码将获得更加详细的信息:
DIBSECTION ds;
GetObject(hBmp,sizeof(ds),&ds);
DIBSECTION 的定义请参考 MSDN。
2.3 载入DIB
LoadBitmap 从资源里载入位图
LoadImage 从资源或文件里载入位图
2.4 访问位图数据
GetBitmapBits和SetBitmapBits可用于访问位图中的像素数据。这两个函数不能获得颜色表,属于过时的函数。
GetDIBits和SetDIBits是GetBitmapBits和SetBitmapBits的升级版本,它们增加了处理颜色表的功能。
2.5 位图传输
位图传输主要有如下几个函数
BitBlt
MaskBlt
PlgBlt
StretchBlt
TransparentBlt
上面的函数StretchBlt,可以用SetStretchBltMode设置模式,用GetStretchBltMode获取模式。
上面几个函数都是在两个HDC之间传输位图,下面两个函数是将DIB位图直接传输到HDC上:
SetDIBitsToDevice
StretchDIBits
第3章示例代码
3.1 屏幕拷贝
Windows系统里,按下 Print Screen 键即可将屏幕位图拷贝到剪贴板中。下面是实现此功能的 MFC 代码:
HBITMAP CaptureScreen() { int cx = GetSystemMetrics(SM_CXSCREEN); int cy = GetSystemMetrics(SM_CYSCREEN); HDC hDC = GetDC(NULL); HBITMAP hBmp = CreateCompatibleBitmap(hDC,cx,cy); HDC hMem = CreateCompatibleDC(hDC); HGDIOBJ hBmpOld = SelectObject(hMem,hBmp); BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY); SelectObject(hMem,hBmpOld); DeleteDC(hMem); ReleaseDC(NULL,hDC); return hBmp; } void BmpToClipboard(HBITMAP hBmp) { if(OpenClipboard(NULL)) { EmptyClipboard(); SetClipboardData(CF_BITMAP,hBmp); CloseClipboard(); } } void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile) { BITMAP bm; GetObject(hBmp,sizeof(bm),&bm); if(0 == wBitsPixel) { wBitsPixel = bm.bmBitsPixel; } DWORD dwSizeClr = 0; //颜色表字节数 if(wBitsPixel <= 8) { dwSizeClr = (1 << wBitsPixel) * sizeof(RGBQUAD); } //一行的字节数 DWORD dwLineBytes = ((bm.bmWidth * wBitsPixel + 31) & ~31) >> 3; //像素数据字节数 DWORD dwSizeImg = dwLineBytes * bm.bmHeight; BITMAPFILEHEADER bmfh; bmfh.bfType = 0x4D42; //BM bmfh.bfOffBits = sizeof(bmfh) + sizeof(BITMAPINFOHEADER) + dwSizeClr; bmfh.bfSize = bmfh.bfOffBits + dwSizeImg; bmfh.bfReserved1 = 0; bmfh.bfReserved2 = 0; BYTE* pBmpFile = (BYTE*)malloc(bmfh.bfSize); memcpy(pBmpFile,&bmfh,sizeof(bmfh)); LPBITMAPINFO lpbi = (LPBITMAPINFO)(pBmpFile + sizeof(bmfh)); lpbi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); lpbi->bmiHeader.biWidth = bm.bmWidth; lpbi->bmiHeader.biHeight = bm.bmHeight; lpbi->bmiHeader.biPlanes = 1; lpbi->bmiHeader.biBitCount = wBitsPixel; lpbi->bmiHeader.biCompression = BI_RGB; lpbi->bmiHeader.biSizeImage = dwSizeImg; lpbi->bmiHeader.biXPelsPerMeter = 0; lpbi->bmiHeader.biYPelsPerMeter = 0; lpbi->bmiHeader.biClrUsed = 0; lpbi->bmiHeader.biClrImportant = 0; HDC hDC = GetDC(NULL); GetDIBits(hDC,hBmp,0,bm.bmHeight ,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS); ReleaseDC(NULL,hDC); CFile f; f.Open(szFile,CFile::modeWrite | CFile::modeCreate); f.Write(pBmpFile,bmfh.bfSize); f.Close(); free(pBmpFile); } void CTestDlg::OnButton3() { HBITMAP hBmp = CaptureScreen(); if(hBmp) { BmpToClipboard(hBmp); BmpToFile(hBmp, 0,_T("0.bmp")); BmpToFile(hBmp, 1,_T("1.bmp")); BmpToFile(hBmp, 4,_T("4.bmp")); BmpToFile(hBmp, 8,_T("8.bmp")); BmpToFile(hBmp,16,_T("16.bmp")); BmpToFile(hBmp,24,_T("24.bmp")); BmpToFile(hBmp,32,_T("32.bmp")); DeleteObject(hBmp); } } |
当用户单击Button3时,会调用CTestDlg::OnButton3。它做了三项工作:
1、调用CaptureScreen(),将屏幕的DFB转换为DDB;
2、调用BmpToClipboard(hBmp);将DDB复制到剪贴板里;
3、调用BmpToFile(hBmp, 0,_T("0.bmp"));将DDB保存到文件里。
下面逐个进行介绍。
3.1.1 CaptureScreen
屏幕拷贝的实质就是将显存里的DFB提取出来,转换为DDB。
根据图1.1可知,DFB与DDB可通过函数BitBlt相互转换、传输。
现在的问题是:BitBlt只能在两个HDC之间相互传输位图,并不能直接操纵HBITMAP。这里就需要内存HDC。
CaptureScreen的详细讲解如下:
//下面两行代码用来获得屏幕的宽度和高度,单位是像素。
int cx = GetSystemMetrics(SM_CXSCREEN);
int cy = GetSystemMetrics(SM_CYSCREEN);
//下面这行代码用来获得屏幕的HDC
HDC hDC = GetDC(NULL);
//创建与屏幕兼容的DDB
HBITMAP hBmp = CreateCompatibleBitmap(hDC,cx,cy);
//创建与屏幕兼容的内存HDC
HDC hMem = CreateCompatibleDC(hDC);
//把DDB选入内存HDC
HGDIOBJ hBmpOld = SelectObject(hMem,hBmp);
//将DFB传输给DDB
BitBlt(hMem,0,0,cx,cy,hDC,0,0,SRCCOPY);
//恢复内存HDC原来的位图
SelectObject(hMem,hBmpOld);
//销毁内存HDC
DeleteDC(hMem);
//释放屏幕HDC
ReleaseDC(NULL,hDC);
3.1.2 BmpToClipboard
BmpToClipboard函数把DDB复制到系统剪贴板内,代码很简单,不用多解释。
3.1.3 BmpToFile
BmpToFile函数用来把DDB转换为DIB,然后把DIB存入文件里。它的参数如下所示:
void BmpToFile(HBITMAP hBmp,WORD wBitsPixel,LPCTSTR szFile)
第一个参数是DDB;
第二个参数是转换为DIB时的颜色位数。0 表示采用DDB的颜色位数,其它的取值有:1、2、4、8、16、24、32。颜色位数越大,颜色数就越多,生成的文件越大;
第三个参数是保存时的文件名。
BmpToFile函数的关键点有:
1、调用GetDIBits,把DDB的位图数据以DIB的格式提取出来;
2、调用f.Write(pBmpFile,bmfh.bfSize);把DIB数据写入文件。下面就是.bmp文件的格式:
图3.1
首先是一个BITMAPFILEHEADER。pBmpFile指向它;
接着是一个BITMAPINFOHEADER。lpbi指向它;BITMAPINFO的第一个成员变量就是BITMAPINFOHEADER型的,所以也可以把lpbi看做BITMAPINFO*;
对于颜色位数小于16的DIB需要定义颜色表,即定义多个RGBQUAD。对于颜色位数大于等于16的DIB,一般是不需要RGBQUAD的。
lpbi->bmiColors指向颜色表。颜色表的项数为(1 << wBitsPixel),颜色表的字节数为(1 << wBitsPixel) * sizeof(RGBQUAD);
中间还有一个"未定义",它可以是零字节也可以是多个字节,内容随便填。
最后是像素数据,pBmpFile + bmfh.bfOffBits指向它。
现在再看看GetDIBits的代码:
GetDIBits(hDC,hBmp,0,bm.bmHeight
,pBmpFile + bmfh.bfOffBits,lpbi,DIB_RGB_COLORS);
第5个参数pBmpFile + bmfh.bfOffBits指向像素数据;
第6个参数lpbi是一个BITMAPINFO*。lpbi->bmiColors指向颜色表。
调用GetDIBits将改写颜色表(lpbi->bmiColors)和像素数据(pBmpFile + bmfh.bfOffBits)。
3.2 读取位图文件并显示
读取位图文件并显示的代码有两份。
第一份代码如下所示:
void CTestDlg::OnButton4() { HBITMAP hBmp = (HBITMAP)LoadImage(NULL,_T("0.bmp") ,IMAGE_BITMAP,0,0,LR_DEFAULTSIZE | LR_LOADFROMFILE); if(hBmp) { BITMAP bm; GetObject(hBmp,sizeof(bm),&bm); CClientDC dc(this); HDC hMem = CreateCompatibleDC(dc.m_hDC); HGDIOBJ hBmpOld = SelectObject(hMem,hBmp); ::BitBlt(dc.m_hDC,0,0,bm.bmWidth,bm.bmHeight,hMem,0,0,SRCCOPY); SelectObject(hMem,hBmpOld); DeleteDC(hMem); DeleteObject(hBmp); } } |
说明:
1、载入位图使用了LoadImage函数。
2、显示位图的步骤:
1)创建一个与屏幕兼容的内存HDC;
2)将载入的位图选入内存HDC;
3)调用BitBlt将内存HDC的内容传输到屏幕HDC上;
4)销毁内存HDC。
第二份代码如下所示:
void CTestDlg::OnButton5() { CFile f; if(f.Open(_T("1.bmp"),CFile::modeRead)) { DWORD dwSize = f.GetLength(); if(dwSize > sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)) { BYTE* pBmpFile = (BYTE*)malloc(dwSize); f.Read(pBmpFile,dwSize); BITMAPFILEHEADER*pbmfh = (BITMAPFILEHEADER*)pBmpFile; if(pbmfh->bfType == 0x4D42) { BITMAPINFOHEADER*pbmih = (BITMAPINFOHEADER*)(pBmpFile + sizeof(BITMAPFILEHEADER)); CClientDC dc(this); SetDIBitsToDevice(dc.m_hDC ,0,0,pbmih->biWidth,pbmih->biHeight ,0,0,0,pbmih->biHeight ,pBmpFile + pbmfh->bfOffBits ,(BITMAPINFO*)pbmih ,DIB_RGB_COLORS); } free(pBmpFile); } f.Close(); } } |
说明:
1、载入位图就是打开位图文件,然后将所有内容载入到内存;
2、显示位图没有使用BitBlt,而是使用SetDIBitsToDevice直接把DIB传输到屏幕。其中pBmpFile + pbmfh->bfOffBits指向像素数据,(BITMAPINFO*)pbmih指向BITMAPINFO,pbmih->bmiColors指向颜色表。
Windows 位图的更多相关文章
- Windows API 的数据类型与 Delphi 数据类型对照表
Windows 数据类型 Delphi 数据类型 描述 LPSTR PAnsiChar 字符串指针 LPCSTR PAnsiChar 字符串指针 DWORD LongWord 整数 BOOL Long ...
- 【转载】Windows api数据类型
最近在接触windows api函数,看到了很多之前没有看到过的数据类型,发现“个人图书馆”中有个帖子说的挺详细的,特地搬运过来 Windows 数据类型 Delphi 数据类型 描述 LPSTR P ...
- python中提取位图信息(AttributeError: module 'struct' has no attribute 'unstack')
前言 今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用.和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读. 0x00 ...
- windows的各种扩展名详解
Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...
- CreateDIBSection和位图结构
2019独角兽企业重金招聘Python工程师标准>>> 理解分辨率 我们常说的屏幕分辨率为640×480,刷新频率为70Hz,意思是说每行要扫描640个象素,一共有480行,每秒重复 ...
- 学习 opencv---(2) 图像的载入,显示和输出
了解过之前老版本OpenCV的童鞋们都应该清楚,对于OpenCV1.0时代的基于 C 语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会照成内存泄露.而且用起来超 ...
- OpenCV阶段总结扩充。
Mat类型简单介绍 /* cv::Mat类是用于保存图像以及其他矩阵的数据结构.默认情况下,其尺寸为0,我们也可以设置其初始尺寸,比如定义一个Mat类的对象,就要写cv::Mat pic(320,64 ...
- BMP图像数据格式详解
一.简介 BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式.Windows系统内部各图像绘制操作都是以B ...
- cvLoadImage
编辑 本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧! 函数原型:IplImage* cvLoadImage( const char* filename, int flags= ...
随机推荐
- reactjs入门到实战(八)----表单组件的使用
表单组件支持几个受用户交互影响的属性: value,用于 <input>.<textarea> 组件. checked,用于类型为 checkbox 或者 radio 的 &l ...
- offsetHeight 正则表达式验证格式
获取Div 的height width等属性 <%@ Page Language="C#" AutoEventWireup="true" CodeFile ...
- SqlSever基础 where 与 group by组合起来 处理数据
镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...
- iptraf:TCP/UDP网络监控工具
原文:http://www.unixmen.com/iptraf-tcpudp-network-monitoring-utility/ 作者: Enock Seth Nyamador 译文:LCTT ...
- CCNA training notes
5/29: vlan:virtual lan, 通过PVID来将物理上连通的host/PC划分到不同的局域网. switch的每个port有access与trunk两种mode,trunk模式的por ...
- Mysql错误问题记录
① Incorrect string value: '\xE6\x94\xBE\xE5\xA4\xA7...' for column 'name' at row 1 Query…… 原因:编码不匹配. ...
- Using SYSTEM.MOUSE_ITEM In Oracle Forms
If the mouse is in an item, SYSTEM.MOUSE_ITEM represents the name of that item as a CHAR value.For e ...
- Codeforces Round #257 (Div. 2) B
B. Jzzhu and Sequences time limit per test 1 second memory limit per test 256 megabytes input standa ...
- Deep Learning: Activation Function
Sigmoid Function ReLU Function Tanh Function
- getting started with building a ROS simulation platform for Deep Reinforcement Learning
Apparently, this ongoing work is to make a preparation for futural research on Deep Reinforcement Le ...