显示位图,有许多的方法实现。本文主要介绍如何将内存中的与设备无关的位图,即一个内存块(可以是void*)显示在屏幕上。
通常,我们要显示一幅位图,一般是通过Windows API提供的位图贴图函数Bitblt来实现。在MFC中,CDC为我们封装了这个函数,他的原型为:BOOL BitBlt( int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop )。
其中各参数的意义为:
x和y是指定目标矩形左上角的逻辑x坐标和y坐标;
nWidth和nHeight是指定目标矩形和源位图的宽度和高度(逻辑单位);
pSrcDC是标识待拷贝位图的设备上下文,如果dwRop指定不包括源的光栅操作,则它必须为NULL;
xSrc和ySrc是指定源位图左上角的逻辑X坐标和Y坐标;
dwRop是指定要执行的光栅操作。光栅操作代码定义GDC如何合并输出操作中的颜色,包括当前画刷、可能的源位图和目标位图。
一般使用BitBlt的过程为:创建一个内存DC,载入位图并显示。假设位图的宽和高为width和height,显示在(0,0)位置。一般的实现有:
CDC* pDC = GetDC();
CDC hMenDC;
hMenDC.CreateCompatibleDC(pDC);
HBITMAP hBitmap; //?,未知,后面算出
CBitmap Bitmap;
Bitmap.Attach(hBitmap);
CBitmap* pOldBitmap = NULL;
pOldBitmap = hMenDC.SelectObject(&Bitmap);
pDC->BitBlt(0,0, width, height, &hMenDC, 0, 0, SRCCOPY);
hMenDC.SelectObject(pOldBitmap);
Bitmap.Detach();
DeleteObject(hBitmap);
hMenDC.DeleteDC();
ReleaseDC(pDC);
现在的问题是,内存DC需要载入的是一个位图,但是我们的与设备无关的位图DIB却是一个内存块(void*),怎样才能将内存块与一个位图句柄连接起来呢。
在Windows API中,为我们提供了一个函数,可以将一个位图像素位的内存块转为一个位图句柄,这个函数为:
HBITMAP CreateDIBSection(
HDC hdc, // handle to DC
CONST BITMAPINFO *pbmi, // bitmap data
UINT iUsage, // data type indicator
VOID **ppvBits, // bit values
HANDLE hSection, // handle to file mapping object
DWORD dwOffset // offset to bitmap bit values
);
其中,第2个参数是一个BITMAPINFO的数据结构,它的定义为:
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO, *PBITMAPINFO
其中,bmiHeader是一个类型为BITMAPINFOHEADER 的结构,bmiColors[1]是一个调色板的数据结构,由于现在的视频适配器都提供了24位的颜色显示模式,
所以一般这个参数不需要自己去处理,只有小于24位的时候才需要处理。所以为了实现DIB到HBITMAP的转换,就需要定义一个BITMAPINFOHEADER 结构。它的定义为:
typedef struct tagBITMAPINFOHEADER{ DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
因此,就需要实现结构里面的所有成员。如下所示:
LPBITMAPINFOHEADER lpbi = new BITMAPINFOHEADER;
lpbi->biSize = sizeof(BITMAPINFOHEADER);
lpbi->biBitCount = 24; //每个像素的位数:1,4,8,24
lpbi->biClrImportant = 0;
lpbi->biClrUsed = 0;
lpbi->biCompression = BI_RGB;
lpbi->biHeight = -m_Height;
lpbi->biWidth = m_Width;
lpbi->biPlanes = 1;
lpbi->biSizeImage = m_Height*nPitch; //nPitch = ((DIB宽度×24)+31)/32×4(公式,是指每条扫描线含有多少像素)lpbi->biXPelsPerMeter = 0;
lpbi->biYPelsPerMeter = 0;接着就可以调用CreateDIBSection函数来创建位图句柄:
LPSTR lpTargetBits = NULL;
HBITMAP hBitmap = CreateDIBSection(pDC->m_hDC, (LPBITMAPINFO)lpbi,
DIB_RGB_COLORS, (VOID**)&lpTargetBits, NULL, 0);
由于第2个参数中的调色板未用,所以可以直接转换成BITMAPINFO结构,不会出问题。
最后,就可以拿位图来进行贴图操作。