● 绪言返回目录 位图在Windows系统平台中是一种很常见的图像格式,如果从保存图像的性能上来讲它不是最好的,JPG、GIF格式都超过它,但是由于它是微软公司制定的标准,并且已嵌入了操作系统,所以它就显得很重要了。以下的内容是我根据VC5.0的联机帮助翻译而来的,有些地方我加入了自己的观点(都已用括号表明),因为我对位图的知识掌握的也不是太深,所以可能有些地方翻译的不是太准确,甚至是错误,如果哪位高手发现了,请不吝赐教。同时,我也希望这份资料能对那些想了解位图的朋友有所帮助,那是我编译这份资料的最大愿望! ● 关于位图 (About Bitmap)返回目录 位图是能被选入设备描述表(DC)的七种目标之一,其余的六种目标是:笔(pen)、刷(brush)、字体(font)、区域(region)、逻辑调色板(logical palette)、和路径(path)。在微软Win32应用程序界面(API)中,控制面板应用程序就是一种使用位图的程序,当用户为桌面选择壁纸的时候,实质上他选择的就是位图,Windows系统将根据这张位图来绘制桌面。从用户的角度来看,位图只是一个矩形的图像,而在开发者的眼中,位图则是一个结构的集合体。它可能包含以下的一些元素: * 一个描述图像信息的结构,比如图像的大小(宽、高)、位数组的尺寸、创建这张图片的设备的分辩率,等等。 下面的图例可用于说明一个位图的真实位数组内容: 在前面这个范例中,图像是创建于一个VGA的显示器,这种设备使用了一个16色的调色板,而一个16色的调色板至少需要4位的索引值才能表示(2^4),所以,在位图的位数组中,每个像素都是用4位(半个字节)来表示索引值的。 ● 位图的类型 (Bitmap Types)返回目录 位图一共有两种类型,即:设备相关位图(DDB)和设备无关位图(DIB)。DDB位图在早期的Windows系统(Windows 3.0以前)中是很普遍的,事实上它也是唯一的。然而,随着显示器制造技术的进步,以及显示设备的多样化,DDB位图的一些固有的问题开始浮现出来了。比如,它不能够存储(或者说获取)创建这张图片的原始设备的分辩率,这样,应用程序就不能快速的判断客户机的显示设备是否适合显示这张图片。为了解决这一难题,微软创建了DIB位图格式。 设备无关位图 (Device-Independent Bitmap) DIB位图包含下列的颜色和尺寸信息: 以上这些信息保存在BITMAPINFO结构中,该结构由BITMAPINFOHEADER结构和两个或更多个RGBQUAD结构所组成。BITMAPINFOHEADER结构所包含的成员表明了图像的尺寸、原始设备的颜色格式、以及数据压缩方案等信息。RGBQUAD结构标识了像素所用到的颜色数据。 DIB位图也有两种形式,即:底到上型DIB(bottom-up),和顶到下型DIB(top-down)。底到上型DIB的原点(origin)在图像的左下角,而顶到下型DIB的原点在图像的左上角。如果DIB的高度值(由BITMAPINFOHEADER结构中的biHeight成员标识)是一个正值,那么就表明这个DIB是一个底到上型DIB,如果高度值是一个负值,那么它就是一个顶到下型DIB。注意:顶到下型的DIB位图是不能被压缩的。 位图的颜色格式是通过颜色面板值(planes)和颜色位值(bitcount)计算得来的,颜色面板值永远是1,而颜色位值则可以是1、4、8、16、24、32其中的一个。如果它是1,则表示位图是一张单色位图(译者注:通常是黑白位图,只有黑和白两种颜色,当然它也可以是任意两种指定的颜色),如果它是4,则表示这是一张VGA位图,如果它是8、16、24、或是32,则表示该位图是其他设备所产生的位图。如果应用程序想获取当前显示设备(或打印机)的颜色位值(或称位深度),可调用API函数GetDeviceCaps(),并将第二个参数设为BITSPIXEL即可。 设备相关位图 (Device-Dependent Bitmaps) 设备相关位图(DDB)之所以现在还被系统支持,只是为了兼容旧的Windows 3.0软件,如果程序员现在要开发一个与位图有关的程序,则应该尽量使用或生成DIB格式的位图。 ● 位图、设备描述表、和绘图表面 (Bitmaps, Device Contexts, and Drawing Surfaces) 返回目录 设备描述表(DC)是一个定义图形目标的数据结构,包括图形的属性、映射模式等信息。应用程序可以调用CreateDC()函数来创建一个DC,它也可以调用GetDC()函数来获取一个窗口的DC。 绘图表面 在应用程序获得一个DC句柄之前,窗口将先选择一个“绘图表面”到DC中。如果应用程序是在一个VGA的显示设备上调用的CreateDC()函数,那么它获得的DC绘图表面尺寸就是640×480像素。如果应用程序是调用的GetDC()函数,则绘图表面的尺寸就等于窗口客户区的尺寸。 兼容设备描述表 (Compatible Device Contexts) 先将图形在内存中画好,再一次性的输出到真实的显示设备上要比多次向显示设备上输出图形要快,并且程序更容易设计。为此,微软专门提供了一种特殊的设备描述表,称为兼容设备描述表(Compatible Device Context)(译者注:也叫作内存设备描述表)。Windows系统把兼容DC视为一种存在于内存中的虚拟设备。实质上兼容DC就是内存中的一个数组,应用程序可以把位图的颜色数据(即像素)保存于兼容DC中。 ● 位图的旋转 (Bitmap Rotation) 返回目录 Windows提供了一个函数,用于将矩形的位图拷贝到一个平行四边形中,这个函数就是PlgBlt()。在转换时,要将矩形位图DC作为源DC,而将平行四边形DC作为目标DC。有关旋转和World units(译者注:这个词未译出)的资料请参见VC联机帮助中Coordinate Spaces 和 Transformations 中的内容。 ● 位图的比例缩放 (Bitmap Scaling) 返回目录 Win32 API也提供了一个按比例缩放位图的函数,它就是StretchBlt()。它可以将源设备描述表(DC)中的矩形位图传送到目标设备描述表中。但不同于BitBlt()函数,StretchBlt()允许应用程序指定源、目标位图的尺寸。如果应用程序指定的目标位图尺寸比源位图尺寸小,则系统将压缩源位图以符合目标位图尺寸。同时,你也可以指定该函数的压缩方式,使用SetStretchBitMode()函数。当目标位图的尺寸大于源位图时,系统将拉伸源位图(即扩大像素的颜色范围)以符合要求。 ● 作为画刷的位图 (Bitmaps as Brushes) 返回目录 Win32 API中有几个函数是使用已选入DC中的画刷来进行位图操作的,比如:PatBlt()函数可在窗口的一个矩形区域中复制画刷,而FloodFill()函数则可以在窗口的一个非矩形区域中复制画刷(这个非矩形区域可以用指定颜色来圈定)。 ROP 描述 PATCOPY 拷贝图样到目标位图中 PATINVERT 用图样的像素或(即位操作OR)目标位图 DSTINVERT 将目标位图的像素值取反(即非目标图的像素值) BLACKNESS 将所有的输出都设为二进制的0 WHITENESS 将所有的输出都设为二进制的1 不同于PatBlt()函数,FloodFill()函数只是将选入DC的画刷简单的重复复制到一个由用户用指定颜色圈定的不规则区域中(填充满为止)。它不接受ROP命令。
● 位图的存储 (Bitmap Storage) 返回目录 如果用户想将位图保存到一个文件中,那么应用程序必需为该文件取一个以.BMP为后缀的扩展名,然后将位图以Windows位图文件格式保存到该文件当中。Windows位图文件格式由几部分组成,下表说明: 其中第一个结构BITMAPFILEHEADER包括了一些对位图文件的大致说明,比如:位图文件的标志“BM”、位图文件的尺寸、位数组的偏移等信息。第二个结构BITMAPINFOHEADER则指明了位图图像的宽度、高度、颜色格式(位面数,位深度)、压缩标志、原始设备分辩率等信息。再接下来的是RGBQUAD结构数组,它里面放置的是位图的调色板数据,每一个结构描述一个调色板项(包括红、绿、蓝的分量值)。(译者注:有些位图没有这个RGBQUAD数组,比如16位、24位、32位位图。而且有些位图文件将该数组所占空间移做它用,如16位、32位位图的BI_BITFIELD格式就将该空间作为三色掩码的存放处,编程时应注意区别)。 结构 对应的字节范围 BITMAPFILEHEADER 0x00 - 0x0D BITMAPINFOHEADER 0x0E - 0x35 RGBQUAD array 0x36 - 0x75 Color-index array 0x76 - 0x275 (译者注:Color-index array实际上就是位图的位数组)
● 使用位图 (Using Bitmap) 返回目录 ● 捕获图像 (Capturing an Image) 返回目录 你可以用位图来捕获图像,并且可以将已捕获的图像保存到文件中,或是在窗口的其他位置显示该图像。在某些应用程序中,有些时候你也必需临时性的保存屏幕上的图像,比如在一个绘图程序中,如果用户选择了放大命令,那么你必需先保存当前屏幕上的图像,然后将图像放大,当用户再返回正常图像时,你就可以用原来保存的图像恢复到屏幕上了。 /* * Create a normal DC and a memory DC for the entire screen. The * normal DC provides a "snapshot" of the screen contents. The * memory DC keeps a copy of this "snapshot" in the associated * bitmap. */ hdcScreen = CreateDC("DISPLAY", NULL, NULL, NULL); hdcCompatible = CreateCompatibleDC(hdcScreen); /* Create a compatible bitmap for hdcScreen. */ hbmScreen = CreateCompatibleBitmap(hdcScreen, GetDeviceCaps(hdcScreen, HORZRES), GetDeviceCaps(hdcScreen, VERTRES)); if (hbmScreen == 0) errhandler("hbmScreen", hwnd); /* Select the bitmaps into the compatible DC. */ if (!SelectObject(hdcCompatible, hbmScreen)) errhandler("Compatible Bitmap Selection", hwnd); /* Hide the application window. */ ShowWindow(hwnd, SW_HIDE); /* * Copy color data for the entire display into a * bitmap that is selected into a compatible DC. */ if (!BitBlt(hdcCompatible, 0,0, bmp.bmWidth, bmp.bmHeight, hdcScreen, 0,0, SRCCOPY)) errhandler("Screen to Compat Blt Failed", hwnd); /* Redraw the application window. */ ShowWindow(hwnd, SW_SHOW);
● 缩放图像 (scaling an Image) 返回目录 有一些应用程序(比如绘图软件)需要对图像进行放大或缩小处理,这时,应用程序就可以通过调用StretchBlt()函数来达到目的。与BitBlt()函数相同,StretchBlt()函数也是将源DC中的位图拷贝到目标DC中。但是,与BitBlt()函数不同的是,在StretchBlt()函数的入口参数中,应用程序可以指定源位图和目标位图的尺寸。如果指定的源位图尺寸大于指定的目标位图尺寸,则位图将被压缩,反之,位图将被放大。 伸缩模式码 含意 BLACKONWHITE 把待删除的像素与保留的像素进行逻辑与(AND)操作 WHITEONBLACK 把待删除的像素与保留的像素进行逻辑或(OR)操作 COLORONCOLOR 彻底删除待删除的像素 HALFTONE 以目标的颜色数据逼近源像素 你如果想设置伸缩模式,可调用SetStretchBltMode()函数。 下面的范例代码是从一个能将源图像放大两倍的程序中截取下来的(该应用程序使用的是缺省的伸缩模式,所以没有使用SetStretchBltMode()函数)。 hdcScaled = CreateCompatibleDC(hdcScreen); hbmScaled = CreateCompatibleBitmap(hdcScreen, GetDeviceCaps(hdcScreen, HORZRES) * 2, GetDeviceCaps(hdcScreen, VERTRES) * 2); if (hbmScaled == 0) errhandler("hbmScaled", hwnd); /* Select the bitmaps into the compatible DC. */ if (!SelectObject(hdcScaled, hbmScaled)) errhandler("Scaled Bitmap Selection", hwnd); case WM_COMMAND: /* message: command from application menu */ switch(wParam) { case IDM_SCALEX1: if (fBlt){ fScaled = FALSE; hdcWin = GetDC(hwnd); BitBlt(hdcWin, 0,0, bmp.bmWidth, bmp.bmHeight, hdcCompatible, 0,0, SRCCOPY); ReleaseDC(hwnd, hdcWin); } break; case IDM_SCALEX2: if (fBlt){ fScaled = TRUE; StretchBlt(hdcScaled, 0, 0, bmp.bmWidth * 2, bmp.bmHeight * 2, hdcCompatible, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY); hdcWin = GetDC(hwnd); BitBlt(hdcWin, 0,0, bmp.bmWidth, bmp.bmHeight, hdcScaled, 0,0, SRCCOPY); ReleaseDC(hwnd, hdcWin); } break; ● 存储一幅图像 (Storing an Image) 返回目录 很多应用程序都需要将图像保存到文件当中,比如绘图程序要保存用户所画的图片,电子表格程序要保存用户的图表,CAD软件要保存图形,等等。 PBITMAPINFO CreateBitmapInfoStruct(HWND hwnd, HBITMAP hBmp) { BITMAP bmp; PBITMAPINFO pbmi; WORD cClrBits; /* Retrieve the bitmap's color format, width, and height. */ if (!GetObject(hBmp, sizeof(BITMAP), (LPSTR)&bmp)) errhandler("GetObject", hwnd); /* Convert the color format to a count of bits. */ cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel); if (cClrBits == 1) cClrBits = 1; else if (cClrBits <= 4) cClrBits = 4; else if (cClrBits <= 8) cClrBits = 8; else if (cClrBits <= 16) cClrBits = 16; else if (cClrBits <= 24) cClrBits = 24; else cClrBits = 32; /* * Allocate memory for the BITMAPINFO structure. (This structure * contains a BITMAPINFOHEADER structure and an array of RGBQUAD data * structures.) */ if (cClrBits != 24) pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (2^cClrBits)); /* There is no RGBQUAD array for the 24-bit-per-pixel format. */ else pbmi = (PBITMAPINFO) LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)); /* Initialize the fields in the BITMAPINFO structure. */ pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = bmp.bmWidth; pbmi->bmiHeader.biHeight = bmp.bmHeight; pbmi->bmiHeader.biPlanes = bmp.bmPlanes; pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel; if (cClrBits < 24) pbmi->bmiHeader.biClrUsed = 2^cClrBits; /* If the bitmap is not compressed, set the BI_RGB flag. */ pbmi->bmiHeader.biCompression = BI_RGB; /* * Compute the number of bytes in the array of color * indices and store the result in biSizeImage. */ pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) /8 * pbmi->bmiHeader.biHeight * cClrBits; /* * Set biClrImportant to 0, indicating that all of the * device colors are important. */ pbmi->bmiHeader.biClrImportant = 0; return pbmi; }
下面的范例将演示怎样打开一个文件,并拷贝数组、获取调色板索引、初始化保留结构、关闭文件等操作: void CreateBMPFile(HWND hwnd, LPTSTR pszFile, PBITMAPINFO pbi, HBITMAP hBMP, HDC hDC) { HANDLE hf; /* file handle */ BITMAPFILEHEADER hdr; /* bitmap file-header */ PBITMAPINFOHEADER pbih; /* bitmap info-header */ LPBYTE lpBits; /* memory pointer */ DWORD dwTotal; /* total count of bytes */ DWORD cb; /* incremental count of bytes */ BYTE *hp; /* byte pointer */ DWORD dwTmp; pbih = (PBITMAPINFOHEADER) pbi; lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage); if (!lpBits) errhandler("GlobalAlloc", hwnd); /* * Retrieve the color table (RGBQUAD array) and the bits * (array of palette indices) from the DIB. */ if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS)) errhandler("GetDIBits", hwnd); /* Create the .BMP file. */ hf = CreateFile(pszFile, GENERIC_READ | GENERIC_WRITE, (DWORD) 0, (LPSECURITY_ATTRIBUTES) NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE) NULL); if (hf == INVALID_HANDLE_VALUE) errhandler("CreateFile", hwnd); hdr.bfType = 0x4d42; /* 0x42 = "B" 0x4d = "M" */ /* Compute the size of the entire file. */ hdr.bfSize = (DWORD) (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage); hdr.bfReserved1 = 0; hdr.bfReserved2 = 0; /* Compute the offset to the array of color indices. */ hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof (RGBQUAD); /* Copy the BITMAPFILEHEADER into the .BMP file. */ if (!WriteFile(hf, (LPVOID) &hdr, sizeof(BITMAPFILEHEADER), (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) errhandler("WriteFile", hwnd); /* Copy the BITMAPINFOHEADER and RGBQUAD array into the file. */ if (!WriteFile(hf, (LPVOID) pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof (RGBQUAD), (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) errhandler("WriteFile", hwnd); /* Copy the array of color indices into the .BMP file. */ dwTotal = cb = pbih->biSizeImage; hp = lpBits; while (cb > MAXWRITE) { if (!WriteFile(hf, (LPSTR) hp, (int) MAXWRITE, (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) errhandler("WriteFile", hwnd); cb-= MAXWRITE; hp += MAXWRITE; } if (!WriteFile(hf, (LPSTR) hp, (int) cb, (LPDWORD) &dwTmp, (LPOVERLAPPED) NULL)) errhandler("WriteFile", hwnd); /* Close the .BMP file. */ if (!CloseHandle(hf)) errhandler("CloseHandle", hwnd); /* Free memory. */ GlobalFree((HGLOBAL)lpBits);}
● 位图操作函数列表 (Bitmap functions) 返回目录 下面是操作位图的各种函数,有兴趣的朋友可查看Win32 API手册。 BitBlt() CreateBitmap() CreateBitmapIndirect() CreateCompatibleBitmap() CreateDIBitmap() CreateDIBSection() CreateDiscardableBitmap() ExtFloodFill() FloodFill() GetBitmapBits() GetBitmapDimensionEx() GetDIBColorTable() GetDIBits() GetPixel() GetStretchBltMode() LoadBitmap() MaskBlt() PatBlt() PlgBlt() SetBitmapBits() SetBitmapDimensionEx() SetDIBColorTable() SetDIBits() SetDIBitsToDevice() SetPixel() SetPixelV() SetStretchBltMode() StretchBlt() StretchDIBits()
与位图相关的结构 (Bitmap Structures) BITMAP BITMAPCOREHEADER BITMAPCOREINFO BITMAPFILEHEADER BITMAPINFO BITMAPINFOHEADER COLORADJUSTMENT DIBSECTION RGBQUAD RGBTRIPLE SIZE(译者注:以上结构中,BITMAPINFO和BITMAPINFOHEADER的联机帮助内容非常重要,如果你想深入了解BMP的结构,就应该仔细研读一下)
与位图相关的宏 (Bitmap Macros) MAKEROP4 |
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1516532