● 緒言返回目錄 位圖在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