首先明确最主要区别CreateDIBitmap创建的是设备相关位图句柄 - HBITMAP.

                              CreateDIBSection创建的是设备无关位图句柄 - HBITMAP.

DIBDDB之间的相互转换比较慢(关于DIB与DDB区别详见《设备相关(DDB)与设备无关(DIB)》),所以我们使用CreateDIBSection()来创建一个DIB区块。这样作图速度快。 
CreateDIBSection()返回的是一个HBITMAP,CreateDIBitmap()返回的也是HBIT MAP。 
两者的区别在于:CreateDIBSection创建的是一个DIBSECTION结构,
                            CreateDIBitmap创建的是BITMAP结构。 
---------------------------------------------------- 
关于DIB区块(DIBSECTION)详见:http://technet.microsoft.com/zh-cn/library/aa930771
可以看到,它包含了一个 位图结构BITMAP,一个DIB信息头BITMAPINFOHEADER,一个掩码表dsBIT fields[3]. 
还有一个内存映射文件句柄和偏移量。我们不去理睬最后两个字段。 
因此,使用GDI函数对CreateDIBSection()返回的HBITMAP作图是没有什么问题的。 
 
关于CreateDIBSection与CreateDIBitmap函数的对比可参见:http://blog.csdn.net/strikebone/article/details/5832631
 
一、CreateDIBitmap
 
函数功能:该函数由与设备无关的位图(DIB)创建与设备有关的位图(DDB),并且有选择地为位图置位。
所以此函数最终创建的是与设备有关的位图
原型

HBITMAP CreateDIBitmap(HDC hdc,

CONST BITMAPINFOHEADER *lpbmih,

DWORD fdwlnit,

CONST VOID *lpblnit,

CONST BITMAPINFO *lpbmi,

UINT fuUsage);

参数
hdc:设备环境句柄。由于是设备相关的,所以需要指明DC。
lpbmih:指向位图信息头结构的指针,它可以是下列操作系统位图信息头之一:
BITMAPINFOHEADER  Windows NT 3.51和早期使用。
BITMAPV4HEADER      Windows NT 4.0和Windows 95使用。
BITMAPV5HEADER      Windows NT 5.0和Windows 98使用。
如果fdwlnit是CBM_INIT,那么此函数使用此信息息头结构来获取位图所需的宽度、高度以及其他信息。注意高度若是正数,那么表示是自底向上DIB,而负数表示为自顶向下DIB,这种情况与CreateDIBitmap函数兼容。
Fdwlnit:位标识集。它指定系统如何对位图的位进行初始化。
CBM_INIT:如果设置了该标志,那么系统将使用lpblnit和lpbmi两个参数指向的数据来对位图中的位进行初始化。如果没有该标志,那么表示上述两个参数指向的数据无效。如果fdwlnit为0,那么系统不会对位图的位进行初始化。
lpblnit:该指针指向包含初始的位图数据的字节类型数组。数据格式与参数lpbmi指向的BITMAPINFO结构中的成员biBitCount有关。
lpbmi:指向BITMAPINFO结构的旨针。该结构描述了参数lpbmi指向的数组的维数和颜色格式。
fuUsage:表示BITMAPINFO结构的成员bmiColors是否初始化过,并且如果是,那么bmiColors是否包含明确的红、绿、蓝(RGB)值或调色板索引。参数fuUsage必须取下列值中的一个,这些值的含义为:
DIB_PAL_COLORS :表示提供一个颜色表,并且该表由该位图要选入的设备环境的逻辑调色板的16位索引值数组组成。
DIB_RGB_COLORS:表示提供一个颜色表,并且表中包含了原义的RGB值。
 
返回值
如果函数执行成功,返回值则是创建的位图的句柄;如果函数执行失败,那么返回值为NULL,若想获取更多错误信息,请调用GetLastError函数。
 
实例:以下代码完成从位图文件中读取DIB位图,并转换成DDB位图:
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;
}

  

其中使用到了位图信息头结构BITMAPFILEHEADER)及位图信息结构BITMAPINFO)。
两者的关系详见《BITMAPFILEHEADER 与 BITMAPINFO》。
 
再看一个使用CreateDIBitmap函数如何由裸像素数据得到位图句柄 - 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);
}
 
一、CreateDIBSection
函数功能:该函数创建应用程序可以直接写入的、与设备无关的位图(DIB)。该函数返回一个位图句柄。

 

函数原型:

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);

其中pImageData即是已知的图像裸数据。

注意

这里想说声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

[转]CreateDIBitmap与CreateDIBSection的更多相关文章

  1. (转载博文)VC++API速查

    窗口处理 2.1 窗口简介 2.2.1 创建普通窗口(CreateWindow.CreateWindowEx) 2.2.2 关闭窗口(CloseWindow) 2.2.3 销毁窗口(DestroyWi ...

  2. CreateDIBSection函数

    HBITMAP CreateDIBSection( HDC hdc, // handle to DC CONSTBITMAPINFO*pbmi, // bitmap dataUINT iUsage, ...

  3. CreateDIBSection和位图结构

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

  4. Windows API 函数列表 附帮助手册

    所有Windows API函数列表,为了方便查询,也为了大家查找,所以整理一下贡献出来了. 帮助手册:700多个Windows API的函数手册 免费下载 API之网络函数 API之消息函数 API之 ...

  5. 第16章 调色板管理器_16.4 一个DIB位图库的实现(1)

    16.4.1自定义的 DIBSTRUCT结构体 字段 含义 PBYTE *ppRow ①指向位图视觉上最上面的一行像素.(不管是自下而上,还是自上而下) ②放在第一个字段,为的是后面定义宏时可方便访问 ...

  6. 第15章 设备无关位图_15.3 DIB和DDB的结合

    第15章 设备相关位图_15.3 DIB和DDB的结合 15.3.1 从DIB创建DDB (1)hBitmap =CreateDIBitmap(…)——注意这名称会误导,实际上创建的是DDB 参数 说 ...

  7. 英文不好也能快速"记忆" API

    英文不好不要紧,把API函数导入打字练习类软件,即是练习打字速度,提高编程效率:也能短时间记忆API. 坚持每天打一遍,约2小时,连续打两周,会对API有很好的记忆,此方法是结合英文学习方法!以下是W ...

  8. Windows 位图

    目录 第1章简介    1 1.1 DFB    1 1.2 DDB    1 1.3 DIB    2 第2章相关API    3 2.1 创建    3 2.1.1 CreateCompatibl ...

  9. DELPHI下API简述(1800个API)

    DELPHI下API简述 http://zero.cnbct.org/show.asp?id=144 auxGetDevCaps API 获取附属设备容量 auxGetNumDevs API 返回附属 ...

随机推荐

  1. 轻量级权限管理系统——MVC基础

    Microsoft Web 开发平台

  2. lintcode-95-验证二叉查找树

    95-验证二叉查找树 给定一个二叉树,判断它是否是合法的二叉查找树(BST) 一棵BST定义为: 节点的左子树中的值要严格小于该节点的值. 节点的右子树中的值要严格大于该节点的值. 左右子树也必须是二 ...

  3. exit和die的区别

    网上搜索die与exit两个函数的区别,大部分的"标准答案"都是说die是退出并释放内存,exit是退出但不释放内存. 这个解释显然是错的,PHP手册中已经说过"die ...

  4. EF to linq 左连接

    如果连接的数据不存在用 null 表示,则可以左连接查询,但是如果数据类型为 int 则会出错. var ng = (from g in _db.NET_NEWS_GROUP join z in _d ...

  5. BZOJ4456 ZJOI2016旅行者(分治+最短路)

    感觉比较套路,每次在长边中轴线处切一刀,求出切割线上的点对矩形内所有点的单源最短路径,以此更新每个询问,递归处理更小的矩形.因为若起点终点跨过中轴线是肯定要经过的,而不跨过中轴线的则可以选择是否经过中 ...

  6. 【题解】ZJOI2008骑士

    树型打牌:洛谷P2607 这道题目一开始没有想到解法,只是想到没有上司的舞会,觉得十分的类似呀. 之后发现:n个点,n条边,只要删去一条边,就变成了和上题一模一样的做法. 那么考虑删去的这条边,实际上 ...

  7. [HDU5956]The Elder

    题面在这里 题意 一个王国中的所有城市构成了一棵有根树,其根节点为首都,编号为1 树有边权,城市的记者每次向祖先移动\(d\)的路程需要的代价为\(d^2\), 如果祖先不是根还需要加上\(p\),求 ...

  8. [LOJ #2538][PKUWC 2018]Slay the Spire

    题目大意:一开始有 $2n$ 张牌,每张牌上都写着一个数字 $w_i$,有两种的牌,每种类型各 $n$ 张: 1.攻击牌:打出后对对方造成牌上的数字的伤害. 2.强化牌:打出后,设数字为 $x$,则其 ...

  9. WPS是个坑

    WPS2016 10.1.0.5740 存储的EXCEL表格文件,用PHP mime_content_type函数获取到的mime类型是“application/zip”

  10. [COGS 622] [NOIP2011] 玛雅游戏 模拟

    整个模拟的关键除了打出来就是一个剪枝:对于两个左右相邻的块你不用再走←,因为走→是等效的 #include<cstdio> #include<cstring> #include ...