HBITMAP对位图数据存储的字节对齐
2007-05-30 14:39
393 查看
HBITMAP是常用的GDI对象,而GetDIBits可以从一个HBITMAP对象中获得其对应的位数据。
其原型如下:
int GetDIBits( HDC hdc, // handle to DC
hdc, // handle to DC
HBITMAP hbmp, // handle to bitmap
UINT hbmp, // handle to bitmap
UINT uStartScan, // first scan line to set
UINT cScanLines, // number of scan lines to copy
LPVOID cScanLines, // number of scan lines to copy
LPVOID lpvBits, // array for bitmap bits
LPBITMAPINFO lpbi, // bitmap data buffer
UINT lpbi, // bitmap data buffer
UINT uUsage // RGB or palette index );
标准的GetDIBits调用方式是两次调用:
第一次传入空的 lpvBits,此时的lpbi作为传出参数,从中可以获得lpvBits所需的内存区域大小。
BYTE *lpvBits = NULL;
BITMAPINFO bmpInfo = {0};
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
/* 第一次调用GetDIBits获得bmpInfo */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, NULL, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for bmpInfo failed %d/n"), nRet);
goto err;
}
以上调用如果成功,就会在bmpInfo.bmiHeader.biSizeImage记录位数据所占的内存区大小,
此时就可以动态分配内存,然后再次调用GetDIBits获得实际的位数据:
lpvBits= new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (NULL == pBitsBuffer) {
nRet = -1;
TRACE( _T("Allocate memory for lpvBits failed/n"));
goto err;
}
/* 第二次调用GetDIBits获得位图数据 */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, lpvBits, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for lpvBits failed %d/n"), nRet);
goto err;
}
很多时候,因为位图的长宽和颜色深度都是已知的,因此位数据所占的内存区大小可以由公式
width * heigth * pixelBits / 8 计算获得
那是否可以省略第一步调用呢?
答案是:最好不要
原因是 bmpInfo.bmiHeader.biSizeImage 并不一定等于 width * heigth * pixelBits / 8
经过多次测试发现,对于16位颜色深度的位图,如果其宽度为奇数,则第一次GetDIBits获得
的位数据大小为 (width+1) * heigth * pixelBits / 8
也就是说对于16位颜色深度的位图,HBITMAP对象对其进行存储时,自动将宽度调整为了偶
数,也即将每行数据对齐到4字节(一个DWORD的长度)。
暂时没有查到相关的解释,我的猜想是这是为了在进行像素操作时能直接以DWORD为单位,
这样比以WORD为单位能减少一半的操作次数。
其原型如下:
int GetDIBits( HDC hdc, // handle to DC
hdc, // handle to DC
HBITMAP hbmp, // handle to bitmap
UINT hbmp, // handle to bitmap
UINT uStartScan, // first scan line to set
UINT cScanLines, // number of scan lines to copy
LPVOID cScanLines, // number of scan lines to copy
LPVOID lpvBits, // array for bitmap bits
LPBITMAPINFO lpbi, // bitmap data buffer
UINT lpbi, // bitmap data buffer
UINT uUsage // RGB or palette index );
标准的GetDIBits调用方式是两次调用:
第一次传入空的 lpvBits,此时的lpbi作为传出参数,从中可以获得lpvBits所需的内存区域大小。
BYTE *lpvBits = NULL;
BITMAPINFO bmpInfo = {0};
bmpInfo.bmiHeader.biSize = sizeof(bmpInfo.bmiHeader);
/* 第一次调用GetDIBits获得bmpInfo */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, NULL, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for bmpInfo failed %d/n"), nRet);
goto err;
}
以上调用如果成功,就会在bmpInfo.bmiHeader.biSizeImage记录位数据所占的内存区大小,
此时就可以动态分配内存,然后再次调用GetDIBits获得实际的位数据:
lpvBits= new BYTE[bmpInfo.bmiHeader.biSizeImage];
if (NULL == pBitsBuffer) {
nRet = -1;
TRACE( _T("Allocate memory for lpvBits failed/n"));
goto err;
}
/* 第二次调用GetDIBits获得位图数据 */
nRet = ::GetDIBits(hDC, hBitmap, 0, pBmpData->bmHeight, lpvBits, &bmpInfo, DIB_RGB_COLORS);
if (nRet == 0) {
nRet = GetLastError();
TRACE( _T("GetDIBits for lpvBits failed %d/n"), nRet);
goto err;
}
很多时候,因为位图的长宽和颜色深度都是已知的,因此位数据所占的内存区大小可以由公式
width * heigth * pixelBits / 8 计算获得
那是否可以省略第一步调用呢?
答案是:最好不要
原因是 bmpInfo.bmiHeader.biSizeImage 并不一定等于 width * heigth * pixelBits / 8
经过多次测试发现,对于16位颜色深度的位图,如果其宽度为奇数,则第一次GetDIBits获得
的位数据大小为 (width+1) * heigth * pixelBits / 8
也就是说对于16位颜色深度的位图,HBITMAP对象对其进行存储时,自动将宽度调整为了偶
数,也即将每行数据对齐到4字节(一个DWORD的长度)。
暂时没有查到相关的解释,我的猜想是这是为了在进行像素操作时能直接以DWORD为单位,
这样比以WORD为单位能减少一半的操作次数。
相关文章推荐
- 字节对齐--结构体数据类型的存储
- 位图四字节对齐问题
- VC中结构体内存分配问题透析(“字节对齐”访问数据)
- 图像数据每行对齐到4字节
- 关于struct和union存储的内存字节对齐的问题
- 巧用redis位图存储亿级数据与访问
- 以字节为单位显示变量的内存存储数据
- 位图4字节对齐问题
- 使用utf8mb4字符集编码支持mysql的四字节字符串(表情符号)存储数据
- 关于字节对齐存储的问题
- c++类不含有数据成员(静态除外),则不会为该类实例分配存储空间,该类实例只占用一个字节空间
- C/C++基本数据类型所占字节数及内存字节对齐
- [oracle] Oracle存储过程里操作BLOB的字节数据的办法,例如写入32位整数
- 用程序段实现,将BUF缓冲区中的100个字节数据依向反次序传送到存储区STRBUF中
- 设某计算机的逻辑地址空间和物理地址空间均为128KB,按字节编址。若某进程最多需要6页数据存储空间,页面大小为1KB,操作系统采用固定分配局部置换策略为该进程分配4个页框(物理块)。
- 1怎么理解,说int存的是“32位整型”数据? 2一个“字节”多少“位” 3“字节”是存储的单位,那么计算机是怎么存储的?即“位”吗?
- 数据对齐---C语言:内存字节对齐详解
- 数据结构基础之memset---有memset 抛出的int 和 char 之间的转换和字节对齐
- C/C++基本数据类型占用字节数及内存字节对齐
- 字节存储数据