目录

第1章简介    1

1.1 DFB    1

1.2 DDB    1

1.3 DIB    2

第2章相关API    3

2.1 创建    3

2.1.1 CreateCompatibleBitmap    3

2.1.2 CreateBitmap    3

2.1.3 CreateBitmapIndirect    4

2.1.4 CreateDIBitmap    4

2.1.5 CreateDIBSection    5

2.1.6 小结    6

2.2 查询    6

2.3 载入DIB    6

2.4 访问位图数据    7

2.5 位图传输    7

第3章示例代码    8

3.1 屏幕拷贝    8

3.1.1 CaptureScreen    10

3.1.2 BmpToClipboard    11

3.1.3 BmpToFile    11

3.2 读取位图文件并显示    12

第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 位图的更多相关文章

  1. Windows API 的数据类型与 Delphi 数据类型对照表

    Windows 数据类型 Delphi 数据类型 描述 LPSTR PAnsiChar 字符串指针 LPCSTR PAnsiChar 字符串指针 DWORD LongWord 整数 BOOL Long ...

  2. 【转载】Windows api数据类型

    最近在接触windows api函数,看到了很多之前没有看到过的数据类型,发现“个人图书馆”中有个帖子说的挺详细的,特地搬运过来 Windows 数据类型 Delphi 数据类型 描述 LPSTR P ...

  3. python中提取位图信息(AttributeError: module 'struct' has no attribute 'unstack')

    前言 今天这篇博文有点意思,它是从一个例子出发,从而体现出在编程中的种种细节和一些知识点的运用.和从前一样,我是人,离成神还有几十万里,所以无可避免的出现不严谨的地方甚至错误,请酌情阅读. 0x00 ...

  4. windows的各种扩展名详解

    Windows系统文件按照不同的格式和用途分很多种类,为便于管理和识别,在对文件命名时,是以扩展名加以区分的,即文件名格式为: 主文件名.扩展名.这样就可以根据文件的扩展名,判定文件的种类,从而知道其 ...

  5. CreateDIBSection和位图结构

    2019独角兽企业重金招聘Python工程师标准>>> 理解分辨率 我们常说的屏幕分辨率为640×480,刷新频率为70Hz,意思是说每行要扫描640个象素,一共有480行,每秒重复 ...

  6. 学习 opencv---(2) 图像的载入,显示和输出

    了解过之前老版本OpenCV的童鞋们都应该清楚,对于OpenCV1.0时代的基于 C 语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会照成内存泄露.而且用起来超 ...

  7. OpenCV阶段总结扩充。

    Mat类型简单介绍 /* cv::Mat类是用于保存图像以及其他矩阵的数据结构.默认情况下,其尺寸为0,我们也可以设置其初始尺寸,比如定义一个Mat类的对象,就要写cv::Mat pic(320,64 ...

  8. BMP图像数据格式详解

    一.简介 BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式.Windows系统内部各图像绘制操作都是以B ...

  9. cvLoadImage

    编辑 本词条缺少名片图,补充相关内容使词条更完整,还能快速升级,赶紧来编辑吧! 函数原型:IplImage* cvLoadImage( const char* filename, int flags= ...

随机推荐

  1. reactjs入门到实战(八)----表单组件的使用

    表单组件支持几个受用户交互影响的属性: value,用于 <input>.<textarea> 组件. checked,用于类型为 checkbox 或者 radio 的 &l ...

  2. offsetHeight 正则表达式验证格式

    获取Div 的height width等属性 <%@ Page Language="C#" AutoEventWireup="true" CodeFile ...

  3. SqlSever基础 where 与 group by组合起来 处理数据

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  4. iptraf:TCP/UDP网络监控工具

    原文:http://www.unixmen.com/iptraf-tcpudp-network-monitoring-utility/ 作者: Enock Seth Nyamador 译文:LCTT  ...

  5. CCNA training notes

    5/29: vlan:virtual lan, 通过PVID来将物理上连通的host/PC划分到不同的局域网. switch的每个port有access与trunk两种mode,trunk模式的por ...

  6. Mysql错误问题记录

    ① Incorrect string value: '\xE6\x94\xBE\xE5\xA4\xA7...' for column 'name' at row 1 Query…… 原因:编码不匹配. ...

  7. 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 ...

  8. Codeforces Round #257 (Div. 2) B

    B. Jzzhu and Sequences time limit per test 1 second memory limit per test 256 megabytes input standa ...

  9. Deep Learning: Activation Function

    Sigmoid Function ReLU Function Tanh Function

  10. 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 ...