16位DIB | 16位DIB | 32位DIB | |
红色屏蔽 | 0x00007C00 | 0x0000F800 | 0x00FF0000 |
绿色屏蔽 | 0x000003E0 | 0x000007E0 | 0x0000FF00 |
蓝色屏蔽 | 0x0000001F | 0x0000001F | 0x000000FF |
速记为 | 5-5-5 | 5-6-5 | 8-8-8 |
BITMAPFILEHEADER
.bfType = 0x4D42
.bfSize = 65078
.bfReserved1 = 0
.bfReserved2 = 0
.bfOffBits = 1078
BITMAPINFOHEADER
.biSize = 40 结构大小
.biWidth = 200 位图宽度,单位为像素(有符号整数)
.biHeight = 320 位图高度,单位为像素(有符号整数)
.biPlanes = 1 颜色平面,目前只有1
.biBitCount = 8 颜色位数(2,4,8,16,24,32)
.biCompression = BI_RGB 压缩算法
.biSizeImage = 64000 指定实际的位图数据占用的字节数
.biXPelsPerMeter = 0 图像的横向分辨率,单位为像素每米(有符号整数
.biYPelsPerMeter = 0 图像的纵向分辨率,单位为像素每米(有符号整数)
.biClrUsed = 256 调色板颜色数
.biClrImportant = 0 0表示颜色都重要
SetDIBitsToDevice函数传回所显示的扫描行的数目。
因此,要呼叫SetDIBitsToDevice来显示整个DIB图像,您需要下列信息:
- hdc目的表面的设备内容句柄
- xDst和yDst图像左上角的目的坐标
- cxDib和cyDibDIB的图素宽度和高度,在这里,cyDib是BITMAPINFOHEADER结构内biHeight字段的绝对值。
- pInfo和pBits指向位图信息部分和图素位的指针
BITMAPFILEHEADER * DibLoadImage (PTSTR pstrFileName)
{
BOOL bSuccess ;
DWORD dwFileSize, dwHighSize, dwBytesRead ;
HANDLE hFile ;
BITMAPFILEHEADER * pbmfh ;
hFile = CreateFile (pstrFileName, GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;
if (hFile == INVALID_HANDLE_VALUE)
return NULL ;
dwFileSize = GetFileSize (hFile, &dwHighSize) ;
if (dwHighSize)
{
CloseHandle (hFile) ;
return NULL ;
}
pbmfh = malloc (dwFileSize) ;
if (!pbmfh)
{
CloseHandle (hFile) ;
return NULL ;
}
// 必须全部加载都内存中,不然显示不出来
bSuccess = ReadFile (hFile, pbmfh, dwFileSize, &dwBytesRead, NULL) ;
// bSuccess = ReadFile(hFile, pbmfh, sizeof(BITMAPFILEHEADER),&dwBytesRead, NULL);
CloseHandle (hFile) ;
if (!bSuccess || (pbmfh->bfType != * (WORD *) "BM")
|| (pbmfh->bfSize != dwFileSize))
{
free (pbmfh) ;
return NULL ;
}
return pbmfh ;
}
static BITMAPFILEHEADER * pbmfh ;
static BITMAPINFO * pbmi ;
static BYTE * pBits ;
pbmi = (BITMAPINFO *) (pbmfh + 1) ;
pBits = (BYTE *) pbmfh + pbmfh->bfOffBits ;
// Get the DIB width and height
//判断是不是旧格式 If 表示旧格式
if (pbmi->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
{
cxDib = ((BITMAPCOREHEADER *) pbmi)->bcWidth ;
cyDib = ((BITMAPCOREHEADER *) pbmi)->bcHeight ;
}
else
{
cxDib = pbmi->bmiHeader.biWidth ;
cyDib = abs (pbmi->bmiHeader.biHeight) ;
}
if (pbmfh)
SetDIBitsToDevice (hdc,
0, // xDst
0, // yDst
cxDib, // cxSrc
cyDib, // cySrc
0, // xSrc 源图坐标系左下角 X轴
0, // ySrc
0, // first scan line
cyDib, // number of scan lines
pBits,
pbmi,
DIB_RGB_COLORS) ;
hFile = CreateFile (szFileName, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_FLAG_SEQUENTIAL_SCAN, NULL) ;
if (hFile == INVALID_HANDLE_VALUE)
{
MessageBox (hwnd, TEXT ("Cannot open file."),
szAppName, MB_ICONWARNING | MB_OK) ;
return 0 ;
}
// Read in the BITMAPFILEHEADER
bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
&dwBytesRead, NULL) ;
if (!bSuccess || dwBytesRead != sizeof (BITMAPFILEHEADER))
{
MessageBox (hwnd, TEXT ("Cannot read file."),
szAppName, MB_ICONWARNING | MB_OK) ;
CloseHandle (hFile) ;
return 0 ;
}
// Check that it's a bitmap
if (bmfh.bfType != * (WORD *) "BM")
{
MessageBox (hwnd, TEXT ("File is not a bitmap."),
szAppName, MB_ICONWARNING | MB_OK) ;
CloseHandle (hFile) ;
return 0 ;
}
// Allocate memory for header and bits
iInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
iBitsSize = bmfh.bfSize - bmfh.bfOffBits ;
pbmi = malloc (iInfoSize) ;
pBits = malloc (iBitsSize) ;
if (pbmi == NULL || pBits == NULL)
{
MessageBox (hwnd, TEXT ("Cannot allocate memory."),
szAppName, MB_ICONWARNING | MB_OK) ;
if (pbmi)
free (pbmi) ;
if (pBits)
free (pBits) ;
CloseHandle (hFile) ;
return 0 ;
}
// Read in the Information Header
bSuccess = ReadFile (hFile, pbmi, iInfoSize,
&dwBytesRead, NULL) ;
if (!bSuccess || (int) dwBytesRead != iInfoSize)
{
MessageBox (hwnd, TEXT ("Cannot read file."),
szAppName, MB_ICONWARNING | MB_OK) ;
if (pbmi)
free (pbmi) ;
if (pBits)
free (pBits) ;
CloseHandle (hFile) ;
return 0 ;
}
// Get the DIB width and height
bTopDown = FALSE ;
if (pbmi->bmiHeader.biSize == sizeof (BITMAPCOREHEADER))
{
cxDib = ((BITMAPCOREHEADER *) pbmi)->bcWidth ;
cyDib = ((BITMAPCOREHEADER *) pbmi)->bcHeight ;
cBits = ((BITMAPCOREHEADER *) pbmi)->bcBitCount ;
}
else
{
if (pbmi->bmiHeader.biHeight < 0)
bTopDown = TRUE ;
cxDib = pbmi->bmiHeader.biWidth ;
cyDib = abs (pbmi->bmiHeader.biHeight) ;
cBits = pbmi->bmiHeader.biBitCount ;
if (pbmi->bmiHeader.biCompression != BI_RGB &&
pbmi->bmiHeader.biCompression != BI_BITFIELDS)
{
MessageBox (hwnd, TEXT ("File is compressed."),
szAppName, MB_ICONWARNING | MB_OK) ;
if (pbmi)
free (pbmi) ;
if (pBits)
free (pBits) ;
CloseHandle (hFile) ;
return 0 ;
}
}
// Get the row length
// 去除小于32的部分 0011 1111 = 31 取反与就去除了 >>3 表示除以8
iRowLength = ((cxDib * cBits + 31) & ~31) >> 3 ;
iRowLength = 4 * ((cxDib * cBits + 31) / 32) ;
// Read and display
SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
ShowCursor (TRUE) ;
hdc = GetDC (hwnd) ;
for (y = 0 ; y < cyDib ; y++)
{
ReadFile (hFile, pBits + y * iRowLength, iRowLength,
&dwBytesRead, NULL) ;
SetDIBitsToDevice (hdc,
0, // xDst
0, // yDst
cxDib, // cxSrc
cyDib, // cySrc
0, // xSrc
0, // ySrc
bTopDown ? cyDib - y - 1 : y,
// first scan line
1, // number of scan lines 表示显示1行
pBits + y * iRowLength,
pbmi,
DIB_RGB_COLORS) ;
}
ReleaseDC (hwnd, hdc) ;
CloseHandle (hFile) ;
ShowCursor (FALSE) ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
return 0 ;
缩放到合适尺寸
SetDIBitsToDevice完成了将DIB的图素对点送入输出设备的显示程序。这对于打印DIB用处不大。打印机的分辨率越高,得到的图像就越小,您最终会得到如邮票大小的图像。
要通过缩小或放大DIB,在输出设备上以特定的大小显示它,可以使用StretchDIBits
iLines = StretchDIBits ( hdc, // device context handle xDst, // x destination coordinate yDst, // y destination coordinate cxDst, // destination rectangle width cyDst, // destination rectangle height xSrc, // x source coordinate ySrc, // y source coordinate cxSrc, // source rectangle width cySrc, // source rectangle height pBits, // pointer to DIB pixel bits pInfo, // pointer to DIB information fClrUse, // color use flag 一般使用 DIB_RGB_COLORS dwRop) ; // raster operation SRCCOPY
从DIB建立DDB
从DIB中建立GDI位图对象可能吗?基本上我们已经知道了方法:如果有DIB,您就能够使用CreateCompatibleBitmap来建立与DIB大小相同并与视讯显示器兼容的GDI位图对象。然后将该位图对象选入内存设备内容并呼叫SetDIBitsToDevice在那个内存DC上绘图。结果就是DDB具有与DIB相同的图像,但具有与视讯显示器兼容的颜色组织。
您也可以通过呼叫CreateDIBitmap用几个步骤完成上述工作。函数使用:hBitmap = CreateDIBitmap (hdc,
(BITMAPINFOHEADER *) (pbmfh + 1),
CBM_INIT,
(BYTE *) pbmfh + pbmfh->bfOffBits,
(BITMAPINFO *) (pbmfh + 1),
DIB_RGB_COLORS) ;
{
GetObject (hBitmap, sizeof (BITMAP), &bitmap) ;
hdcMem = CreateCompatibleDC (hdc) ;
SelectObject (hdcMem, hBitmap) ;
使用BitBlt画
BitBlt (hdc, 0, 0, bitmap.bmWidth, bitmap.bmHeight,
hdcMem, 0, 0, SRCCOPY) ;
DeleteDC (hdcMem) ;
}
DIB区块
我希望您已经对设备相关和设备无关位图的区别有了清晰的概念。DIB能拥有几种色彩组织中的一种,DDB必须是单色的或是与真实输出设备相同的格式。DIB是一个文件或内存块;DDB是GDI位图对象并由位图句柄表示。DIB能被显示或转换为DDB并转换回DIB,但是这里包含了设备无关位和设备相关位之间的转换程序。
现在您将遇到一个函数,它打破了这些规则。该函数在32位Windows版本中发表,称为CreateDIBSection,语法为:
hBitmap = CreateDIBSection ( hdc, // device context handle pInfo, // pointer to DIB information fClrUse, // color use flag ppBits, // pointer to pointer variable hSection, // file-mapping object handle dwOffset) ; // offset to bits in file-mapping object
HBITMAP CreateDibSectionFromDibFile (PTSTR szFileName)
{
BITMAPFILEHEADER bmfh ;
BITMAPINFO * pbmi ;
BYTE * pBits ;
BOOL bSuccess ;
DWORD dwInfoSize, dwBytesRead ;
HANDLE hFile ;
HBITMAP hBitmap ;
// Open the file: read access, prohibit write access
hFile = CreateFile (szFileName, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, NULL) ;
if (hFile == INVALID_HANDLE_VALUE)
return NULL ;
// Read in the BITMAPFILEHEADER
bSuccess = ReadFile (hFile, &bmfh, sizeof (BITMAPFILEHEADER),
&dwBytesRead, NULL) ;
if (!bSuccess || (dwBytesRead != sizeof (BITMAPFILEHEADER))
|| (bmfh.bfType != * (WORD *) "BM"))
{
CloseHandle (hFile) ;
return NULL ;
}
// Allocate memory for the BITMAPINFO structure & read it in
dwInfoSize = bmfh.bfOffBits - sizeof (BITMAPFILEHEADER) ;
pbmi = malloc (dwInfoSize) ;
bSuccess = ReadFile (hFile, pbmi, dwInfoSize, &dwBytesRead, NULL) ;
if (!bSuccess || (dwBytesRead != dwInfoSize))
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
// Create the DIB Section
hBitmap = CreateDIBSection (NULL, pbmi, DIB_RGB_COLORS, &pBits, NULL, 0) ;
if (hBitmap == NULL)
{
free (pbmi) ;
CloseHandle (hFile) ;
return NULL ;
}
// Read in the bitmap bits
ReadFile (hFile, pBits,pbmi->bmiHeader.biSizeImage
/*bmfh.bfSize - bmfh.bfOffBits*/, &dwBytesRead, NULL) ;
free (pbmi) ;
CloseHandle (hFile) ;
return hBitmap ;
}
我之前曾说过,当在视讯显示器上显示DIB时,某些时候必须进行从设备无关图素到设备相关图素的转换,有时这些格式转换可能相当费时。来看一看三种用于显示DIB的方法:
- 当使用SetDIBitsToDevice或StretchDIBits来把DIB直接显示在屏幕上,格式转换在SetDIBitsToDevice或StretchDIBits呼叫期间发生。
- 当使用CreateDIBitmap和(可能是)SetDIBits把DIB转换为DDB,然后使用BitBlt或StretchBlt来显示它时,如果设定了CBM_INIT旗标,格式转换在CreateDIBitmap或SetDIBits期间发生。
- 当使用CreateDIBSection建立DIB区块,然后使用BitBlt或StretchBlt显示它时,格式转换在BitBlt对StretchBlt的呼叫期间发生。