[转]CreateDIBitmap与CreateDIBSection
HBITMAP CreateDIBitmap(HDC hdc,
CONST BITMAPINFOHEADER *lpbmih,
DWORD fdwlnit,
CONST VOID *lpblnit,
CONST BITMAPINFO *lpbmi,
UINT fuUsage);
HBITMAP LoadBitmapEx(LPCTSTR lpszFile) { if(lpszFile == NULL) return NULL; HBITMAP hBitmap; HANDLE hf; BITMAPFILEHEADER* pbmfh; DWORD dwBytesRead, dwFileSize, dwFileSizeHigh; BOOL bSuccess; // 打开一个bmp文件 hf = CreateFile(lpszFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if( hf == INVALID_HANDLE_VALUE) { TRACE("Open file filed with error %d ", GetLastError()); return NULL; } // 得到这个文件大小 dwFileSize = GetFileSize(hf, &dwFileSizeHigh); if( dwFileSizeHigh ) { CloseHandle(hf); return NULL; } // 分配内存,大小为该文件的大小 pbmfh = (BITMAPFILEHEADER*)malloc(dwFileSize); if( !pbmfh ) { CloseHandle(hf); return NULL; } // 读取数据 bSuccess = ReadFile(hf, pbmfh, dwFileSize, &dwBytesRead, NULL); CloseHandle(hf); // 效验文件大小和文件格式 if( !bSuccess || dwFileSize != dwBytesRead || pbmfh->bfType != 0x4D42 || pbmfh->bfSize != dwFileSize) { free((void*)pbmfh); return NULL; } // 进行DIB转换 hBitmap = CreateDIBitmap(GetWindowDC(NULL), (BITMAPINFOHEADER*)(pbmfh + 1), CBM_INIT, (BYTE*)pbmfh + pbmfh->bfOffBits, (BITMAPINFO*)(pbmfh + 1), DIB_RGB_COLORS); free((void*)pbmfh); return hBitmap; }
HBITMAP MakeBitmap(HDC hDc, LPBYTE lpBits, long lWidth, long lHeight, WORD wBitCount) { BITMAPINFO bitinfo; memset(&bitinfo, 0, sizeof(BITMAPINFO)); bitinfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bitinfo.bmiHeader.biWidth = lWidth; bitinfo.bmiHeader.biHeight = lHeight; bitinfo.bmiHeader.biPlanes = 1; bitinfo.bmiHeader.biBitCount = wBitCount; bitinfo.bmiHeader.biCompression = BI_RGB; bitinfo.bmiHeader.biSizeImage = lWidth*lHeight*(wBitCount/8); bitinfo.bmiHeader.biXPelsPerMeter = 96; bitinfo.bmiHeader.biYPelsPerMeter = 96; bitinfo.bmiHeader.biClrUsed = 0; bitinfo.bmiHeader.biClrImportant = 0; return CreateDIBitmap(hDc, &bitinfo.bmiHeader, CBM_INIT, lpBits, &bitinfo, DIB_RGB_COLORS); }
函数原型:
HBITMAP CreateDIBSection(HDC hdc, CONST BITMAPINFO * pbmi, UINT iUsage, VOID * ppvBits, HANDLE hSection, DWORD dwOffset );
参数:
hdc:设备环境句柄。如果iUsage的值是DIB_PAL_COLORS,那么函数使用该设备环境的逻辑调色板对与设备无关位图的颜色进行初始化。
pbmi:指向BITMAPINFO结构的指针,该结构指定了设备无关位图的各种属性,其中包括位图的尺寸和颜色。
iUsage:指定由pbmi参数指定的BITMAPINFO结构中的成员bmiColors数组包含的数据类型(要么是逻辑调色板索引值,要么是原文的RGB值)。下列值是系统定义的,其含义为:
值 |
描述 |
DIB_RGB_COLORS |
根据BITMAPINFOHEADER 的biCompression 成员决定BITMAPINFO 结构包含位掩码还是调色板数组,在呈现位图时使用该数组值。DIB_RGB_COLORS 可以在任何位数的位图上使用。 |
DIB_PAL_COLORS |
BITMAPINFO.bmiColors 数组被取消,在呈现位图时使用目标调色板。DIB_PAL_COLORS只能在8bpp位图中指定。 |
ppvBits:指向一个变量的指针,该变量接收一个指向DIB位数据值的指针。
hSection:该参数设置为NULL。
dwOffset:参数取消。
返回值:
成功,返回值是一个指向刚刚创建的设备无关位图的句柄,并且*ppvBits指向该位图的位数据值;失败,那么返回值为NULL,并且*ppvBit也为NULL,若想获得更多错误信息,请调用GetLastError函数。
备注:
系统为设备独立位图分配内存。如果在之后调用DeleteObject来删除设备独立位图,系统自动关闭内句柄。
在Windows CE 2.0及其以后版本,如果图像是调色板模式(通常是1,2,4和8格式)的,BITMAPINFO 结构中必须包含一个颜色表。对于16bpp或32bpp非调色板图像,颜色表必须是3个入口的长度,这3个入口必须指定红、绿、蓝色掩码。 而且,BITMAPINFOHEADER 结构的biCompression 成员应该被设置为BI_BITFIELDS。 这些位深不支持BI_RGB。 GDI取消24bpp图像的颜色表,他们的像素必须被存储为 蓝-绿-红 (BGR)格式。
Windows CE 不支持332位阈设备。
在Windows CE 1.0 和 1.01版本,pbmi指向的BITMAPINFO结构必须指定每个像素点为1或2位。
实例:下面这段代码实现由已存在的图像裸数据得到CBitmap位图:
CBitmap bitmap ; BITMAPINFO bmpInfo; //创建位图 bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bmpInfo.bmiHeader.biWidth = 480;//宽度 bmpInfo.bmiHeader.biHeight = 480;//高度 bmpInfo.bmiHeader.biPlanes = 1; bmpInfo.bmiHeader.biBitCount = 24; bmpInfo.bmiHeader.biCompression = BI_RGB; UINT uiTotalBytes = 480 * 480 * 3; void *pArray = new BYTE(uiTotalBytes ); HBITMAP hbmp = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, &pArray, NULL, 0);//创建DIB ASSERT(hbmp != NULL); //! 将裸数据复制到bitmap关联的像素区域 memcpy(pArray, pImageData, uiTotalBytes); bitmap.Attach(hbmp);
注意:
这里想说声sorry,之前没太深入理解CreateDIBSection函数的工作机理,在上面的例子中使用new为pArray开辟了一块空间,然后将pArray传入CreateDIBSection函数,其实这种方式是错误的,在Debug下面调试之后发现,你使用new开辟的pArray,
在使用之后并不能正常delete[]掉。CreateDIBSection参数ppvBits正常的使用应该只要传入一个指针即可(就像上面实例中那样
,只要定义一个BYTE *pArray,不需要自己分配空间,直接传给CreateDIBSection即可)。
其实通过ppvBits参数类型(二级指针)也就明白了CreateDIBSection函数的基本使用原理 - CreateDIBSection函数使用hDC及
BITMAPINFO结构信息创建一个指定大小等信息的位图,系统自动为其开辟所需的像素空间,开辟的像素空间地址就是用ppvBits参数
返回的。而且此位图空间系统会自动释放。
之所以需要返回此地址,是因为CreateDIBSection调用时只是根据我们提供的一些信息创建合适大小、格式的位图并开辟控件
,而并不会填充实际像素值(这也是为什么我们直接将指向真实像素值的指针传入此函数中无效的原因),返回这个指针只是为了
之后使用真实像素值填充此段空间(就像上面示例中的memcpy那样)!
1、关于CreateDIBSection函数的使用需要注意一点就是参数ppvBits的使用:传给CreateDIBSection函数的不是一个new
出来一块的空间,也不是直接传入真实像素指针pImageData,而只是传入一个指针变量(如本例中的pArray)用以接收CreateDIBSection自动开辟的像素区域指针!如果你传入了自己手动new出来的一块区域,在Debug调试运行时会发现一个delete []错误,此错误指明你之前new出来的空间无法释放,原因是堆损坏,这部分需要了解下new、delete(或内存检测)原理,详见《new/delete内存分配及释放检测》。
2、像素数据的复制必须在调用了CreateDIBSection函数之后进行,如本例所示,memcpy(pArray, pImageData, uiTotalBytes);是在HBITMAP hbmp = CreateDIBSection(NULL, &bmpInfo, DIB_RGB_COLORS, &pArray, NULL, 0);之后进行的。可能是由于CreateDIBSection函数会对传入的像素空间进行“清除”工作,所以在调用此函数前就先进行像素复制的话,你会发现最终绘制的图像时一片漆黑!
说明:CreateDIBSection函数具有内存映射文件功能,但是一般我们不会使用!所以此处忽略讨论!
此外还需说明的是,有时候在使用CreateDIBSection函数时会发生内存泄露的问题。当初本人开发一个项目时曾经碰到过此问题,在一个窗口进入特定模式后会频繁的调用此函数生成特定的图像,并用此图像去重绘窗口背景。最后调试正是此函数发生了内存泄露,但是代码中确是对产生的HBITMAP及DC进行了释放。当时是使用了另一种方式解决了此问题,但是对于CreateDIBSection函数发生的泄露问题却一直没有深入研究。
虽然如此,对于使用此函数之后善后处理这里仅提几点:
1、最后一次使用完CreateDIBSection函数创建位图之后将其从DC中换出memdc.SelectObject(pOldBmp);
2、在最后一次使用完CreateDIBSection函数创建的位图之后才可delete[] pArray释放掉像素空间。
3、注意释放位图资源bitmap.DeleteObject();
4、释放内存memdc.ReleaseDC();
更多关于CreateDIBSection函数内存使用问题详见:http://blog.sina.com.cn/s/blog_8ec096580101gmmb.html