裝置內容
眾所周知,裝置上下文是一種包含有關某個裝置,如顯示器、印表機等的繪製屬性資訊的WINDOWS資料結構。所有繪製調用都是通過裝置上下文對象進行,其中這些對象中包含了畫筆,位元影像,畫刷等。裝置內容與特定的顯示裝置相關,對於視訊顯示器,裝置內容總是與顯示器上的特定的視窗相關。
裝置內容中的有些值是圖形屬性,這些屬性定義了GDI繪圖函數的工作細節,for example,TextOut,裝置內容的屬性中確定了文字的顏色,文字的背景色,x座標,Y座標映射到所要顯示地區的方式,還有顯示這些文字時系統使用的字型。
當一個程式需要繪圖時,首先要取得裝置內容控制代碼,得到此控制代碼後,Windows用內定的屬性填充內部裝置內容結構中。繪製完畢之後,必須釋放裝置內容控制代碼。
記憶體裝置內容
通過使用一個特別為位元影像操作的裝置環境,可以使得應用程式將輸出操作在記憶體中而不是將其發送到實際的裝置中。記憶體裝置環境使得系統將一部分記憶體當作虛擬設備。它就是一個在記憶體中的位元組,使得應用程式可以使用它為在一個標準的繪圖表面建立位元影像臨時儲存顏色資料。由於位元影像是和裝置相容的,因此記憶體裝置環境在某些時候也就當作相容裝置環境。
記憶體裝置環境作為一個特殊的環境儲存位元影像映像。應用程式可以調用 CreateCompatibleDC 建立一個記憶體裝置環境。
記憶體裝置環境中初始的位元影像只是一個簡單的預留位置。它的尺寸為1*1像素。在應用程式進行繪製之前,必須通過調用 SelectObject 選擇一個恰當寬度和高度的位元影像到該記憶體環境中。要建立一個恰當尺寸的位元影像,可以使用 CreateBitmap , CreateBitmapIndirect , or CreateCompatibleBitmap 三個函數。當位元影像被選進記憶體裝置環境後,系統用一個足夠大的數組取代單位元組來儲存指定的矩形像素的顏色資訊。
當應用程式傳遞一個由 CreateCompatibleDC 返回的控制代碼到繪製函數時,被要求的輸出並不會在裝置的繪製表面顯示。相反,系統會儲存線段,曲線,文字或者地區的顏色資訊到位元組中。應用程式可以複製儲存在記憶體中的映像到繪製表面通過調用 BitBlt 函數,並將記憶體裝置環境作為源裝置環境,視窗或者螢幕裝置環境作為目標裝置環境。
例子1:
/*-------------------------------------------<br /> BITMASK.C -- Bitmap Masking Demonstration<br /> (c) Charles Petzold, 1998<br /> -------------------------------------------*/</p><p>#include <windows.h></p><p>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;</p><p>int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,<br /> PSTR szCmdLine, int iCmdShow)<br />{<br /> static TCHAR szAppName [] = TEXT ("BitMask") ;<br /> HWND hwnd ;<br /> MSG msg ;<br /> WNDCLASS wndclass ;</p><p> wndclass.style = CS_HREDRAW | CS_VREDRAW ;<br /> wndclass.lpfnWndProc = WndProc ;<br /> wndclass.cbClsExtra = 0 ;<br /> wndclass.cbWndExtra = 0 ;<br /> wndclass.hInstance = hInstance ;<br /> wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;<br /> wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;<br /> wndclass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH) ;<br /> wndclass.lpszMenuName = NULL ;<br /> wndclass.lpszClassName = szAppName ;</p><p> if (!RegisterClass (&wndclass))<br /> {<br /> MessageBox (NULL, TEXT ("This program requires Windows NT!"),<br /> szAppName, MB_ICONERROR) ;<br /> return 0 ;<br /> }</p><p> hwnd = CreateWindow (szAppName, TEXT ("Bitmap Masking Demo"),<br /> WS_OVERLAPPEDWINDOW,<br /> CW_USEDEFAULT, CW_USEDEFAULT,<br /> CW_USEDEFAULT, CW_USEDEFAULT,<br /> NULL, NULL, hInstance, NULL) ;</p><p> ShowWindow (hwnd, iCmdShow) ;<br /> UpdateWindow (hwnd) ;</p><p> while (GetMessage (&msg, NULL, 0, 0))<br /> {<br /> TranslateMessage (&msg) ;<br /> DispatchMessage (&msg) ;<br /> }<br /> return msg.wParam ;<br />}</p><p>LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)<br />{<br /> static HBITMAP hBitmapImag, hBitmapMask ;<br /> static HINSTANCE hInstance ;<br /> static int cxClient, cyClient, cxBitmap, cyBitmap ;<br /> BITMAP bitmap ;<br /> HDC hdc, hdcMemImag, hdcMemMask ;<br /> int x, y ;<br /> PAINTSTRUCT ps ;<br /> HBRUSH hBrush;<br /> HBITMAP hBitmap;<br /> RECT rect;</p><p> switch (message)<br /> {<br /> case WM_CREATE:<br /> hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;</p><p> // Load the original image and get its size</p><p> hBitmapImag = LoadBitmap (hInstance, TEXT ("Matthew")) ;<br /> hBrush=CreatePatternBrush(hBitmapImag);<br /> GetObject (hBitmapImag, sizeof (BITMAP), &bitmap) ;<br /> cxBitmap = bitmap.bmWidth ;<br /> cyBitmap = bitmap.bmHeight ;</p><p> // Select the original image into a memory DC</p><p> hdcMemImag = CreateCompatibleDC (NULL) ;<br /> SelectObject (hdcMemImag, hBitmapImag) ;</p><p> // Create the monochrome mask bitmap and memory DC</p><p> hBitmapMask = CreateBitmap (cxBitmap, cyBitmap, 1, 1, NULL) ;<br /> hdcMemMask = CreateCompatibleDC (NULL) ;<br /> SelectObject (hdcMemMask, hBitmapMask) ;</p><p> // Color the mask bitmap black with a white ellipse</p><p> SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;<br /> Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;<br /> SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;<br /> Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;</p><p> // Mask the original image</p><p> BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,<br /> hdcMemMask, 0, 0, SRCAND) ;</p><p> DeleteDC (hdcMemImag) ;<br /> DeleteDC (hdcMemMask) ;<br /> return 0 ;</p><p> case WM_SIZE:<br /> cxClient = LOWORD (lParam) ;<br /> cyClient = HIWORD (lParam) ;<br /> return 0 ;</p><p> case WM_PAINT:<br /> hBitmap = LoadBitmap (hInstance, TEXT ("Matthew")) ;<br /> hBrush=CreatePatternBrush(hBitmap);<br /> hdc = BeginPaint (hwnd, &ps) ;<br /> rect.left=0;<br /> rect.top=0;<br /> rect.right=200;<br /> rect.bottom=320;<br /> Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);<br />FillRect(hdc,&rect,hBrush);<br /> // Select bitmaps into memory DCs</p><p> hdcMemImag = CreateCompatibleDC (hdc) ;<br /> SelectObject (hdcMemImag, hBitmapImag) ;</p><p> hdcMemMask = CreateCompatibleDC (hdc) ;<br /> SelectObject (hdcMemMask, hBitmapMask) ;</p><p> // Center image</p><p> x = (cxClient - cxBitmap) / 2 ;<br /> y = (cyClient - cyBitmap) / 2 ;</p><p> // Do the bitblts</p><p> //BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;<br /> //BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;<br /> BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCCOPY) ;<br /> DeleteDC (hdcMemImag) ;<br /> DeleteDC (hdcMemMask) ;<br /> DeleteObject(hBrush);<br /> EndPaint (hwnd, &ps) ;<br /> return 0 ;</p><p> case WM_DESTROY:<br /> DeleteObject (hBitmapImag) ;<br /> DeleteObject (hBitmapMask) ;<br /> PostQuitMessage (0) ;<br /> return 0 ;<br /> }<br /> return DefWindowProc (hwnd, message, wParam, lParam) ;<br />}<br />
WINDOWS程式設計中位元影像那章的例子稍微修改了一下,運行結果發現,位元影像已經變化了,即不再是剛開始從外部匯入的那個圖片了(圖片的主人是作者查爾斯的侄子),從代碼中可以看到
首先hBitmapImag = LoadBitmap (hInstance, TEXT ("Matthew")) ;載入了一副位元影像
hdcMemImag = CreateCompatibleDC (NULL) ;
SelectObject (hdcMemImag, hBitmapImag) ;並且接著選進了記憶體裝置內容中
其次BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap, hdcMemMask, 0, 0, SRCAND) ;對位元影像進行了修改,此時的修改毫無疑問修改的是記憶體裝置內容中的位元影像,修改的方法很簡單也很巧妙但卻是很有用途的(建立單色位元影像,其實可以藉助這個思路來實現建立透明位元影像的,這裡不多囉嗦)
最後我們在客戶區中央輸出位元影像發現輸出的是修改過的位元影像,另外不要被代碼中建立的位元影像畫刷所幹擾,只是為了結合例子2,達到比較的效果。
可以得出簡單的結論:位元影像被選進記憶體裝置內容中的時候,其實就是選進了位元影像自身,而不是位元影像的副本,因此在記憶體裝置內容中修改位元影像也必然會及時反饋到位元影像本身的。所以輸出的位元影像是變化後的位元影像。
例子2;
BRICKS3.C</p><p>/*-------------------------------------------------------------------------</p><p> BRICKS3.C -- CreatePatternBrush Demonstration</p><p> (c) Charles Petzold, 1998</p><p>---------------------------------------------------------------------------*/</p><p>#include <windows.h></p><p>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;</p><p>int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,</p><p> PSTR szCmdLine, int iCmdShow)</p><p>{</p><p> static TCHAR szAppName [] = TEXT ("Bricks3") ;</p><p> HBITMAP hBitmap ;</p><p> HBRUSH hBrush ;</p><p> HWND hwnd ;</p><p> MSG msg ;</p><p> WNDCLASS wndclass ;</p><p> hBitmap = LoadBitmap (hInstance, TEXT ("Bricks")) ;</p><p> hBrush = CreatePatternBrush (hBitmap) ;</p><p> DeleteObject (hBitmap) ;</p><p> wndclass.style = CS_HREDRAW | CS_VREDRAW ;</p><p> wndclass.lpfnWndProc = WndProc ;</p><p> wndclass.cbClsExtra = 0 ;</p><p> wndclass.cbWndExtra = 0 ;</p><p> wndclass.hInstance = hInstance ;</p><p> wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;</p><p> wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;</p><p> wndclass.hbrBackground = hBrush ;</p><p> wndclass.lpszMenuName = NULL ;</p><p> wndclass.lpszClassName = szAppName ;</p><p> if (!RegisterClass (&wndclass))</p><p> {</p><p> MessageBox ( NULL, TEXT ("This program requires Windows NT!"),</p><p> szAppName, MB_ICONERROR) ;</p><p> return 0 ;</p><p> }</p><p> hwnd = CreateWindow (szAppName, TEXT ("CreatePatternBrush Demo"),</p><p> WS_OVERLAPPEDWINDOW,</p><p> CW_USEDEFAULT, CW_USEDEFAULT,</p><p> CW_USEDEFAULT, CW_USEDEFAULT,</p><p> NULL, NULL, hInstance, NULL) ;</p><p> ShowWindow (hwnd, iCmdShow) ;</p><p> UpdateWindow (hwnd) ;</p><p> while (GetMessage (&msg, NULL, 0, 0))</p><p> {</p><p> TranslateMessage (&msg) ;</p><p> DispatchMessage (&msg) ;</p><p> }</p><p> DeleteObject (hBrush) ;</p><p> return msg.wParam ;</p><p>}</p><p>LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam)</p><p>{</p><p> switch (message)</p><p> {</p><p> case WM_DESTROY:</p><p> PostQuitMessage (0) ;</p><p> return 0 ;</p><p> }</p><p> return DefWindowProc (hwnd, message, wParam, lParam) ;</p><p>}<br />
同樣來自這本書上的一個磚塊例子,用的是位元影像畫刷來實現的。
我們只對三行代碼感興趣:
hBitmap = LoadBitmap (hInstance, TEXT ("Bricks")) ;
hBrush = CreatePatternBrush (hBitmap) ;
DeleteObject (hBitmap) ;
載入位元影像,建立位元影像畫刷,然後立刻刪除到此位元影像。
結果卻是正確的,並沒有因為刪除掉作為畫刷的位元影像而影響畫刷本身。
這個例子也可以得出結論:位元影像被用來建立畫刷,此時畫刷中的位元影像是外部位元影像的一個副本,刪除不刪除互相不影響,因為彼此已經獨立了。
大致可以這樣來體會DC吧:
每個DC都關聯了很多個物件,比如位元影像,畫刷等,DC得到這些對象的指標,以後對DC的操作都是對關聯的對象的操作。
建立畫刷則是根據位元影像的內容,建立一個畫刷,這個畫刷的內容是從位元影像上複製過來的,副本,建立完畢後,這個位元影像跟此畫刷已經沒有任何聯絡了,彼此獨立,刪除位元圖也不會影響到畫刷。
例子3用來證明上述觀點:
/*-------------------------------------------<br /> BITMASK.C -- Bitmap Masking Demonstration<br /> (c) Charles Petzold, 1998<br /> -------------------------------------------*/</p><p>#include <windows.h></p><p>LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;</p><p>int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,<br /> PSTR szCmdLine, int iCmdShow)<br />{<br /> static TCHAR szAppName [] = TEXT ("BitMask") ;<br /> HWND hwnd ;<br /> MSG msg ;<br /> WNDCLASS wndclass ;</p><p> wndclass.style = CS_HREDRAW | CS_VREDRAW ;<br /> wndclass.lpfnWndProc = WndProc ;<br /> wndclass.cbClsExtra = 0 ;<br /> wndclass.cbWndExtra = 0 ;<br /> wndclass.hInstance = hInstance ;<br /> wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;<br /> wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;<br /> wndclass.hbrBackground = (HBRUSH) GetStockObject (LTGRAY_BRUSH) ;<br /> wndclass.lpszMenuName = NULL ;<br /> wndclass.lpszClassName = szAppName ;</p><p> if (!RegisterClass (&wndclass))<br /> {<br /> MessageBox (NULL, TEXT ("This program requires Windows NT!"),<br /> szAppName, MB_ICONERROR) ;<br /> return 0 ;<br /> }</p><p> hwnd = CreateWindow (szAppName, TEXT ("Bitmap Masking Demo"),<br /> WS_OVERLAPPEDWINDOW,<br /> CW_USEDEFAULT, CW_USEDEFAULT,<br /> CW_USEDEFAULT, CW_USEDEFAULT,<br /> NULL, NULL, hInstance, NULL) ;</p><p> ShowWindow (hwnd, iCmdShow) ;<br /> UpdateWindow (hwnd) ;</p><p> while (GetMessage (&msg, NULL, 0, 0))<br /> {<br /> TranslateMessage (&msg) ;<br /> DispatchMessage (&msg) ;<br /> }<br /> return msg.wParam ;<br />}</p><p>LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)<br />{<br /> static HBITMAP hBitmapImag, hBitmapMask ;<br /> static HINSTANCE hInstance ;<br /> static int cxClient, cyClient, cxBitmap, cyBitmap ;<br /> BITMAP bitmap ;<br /> HDC hdc, hdcMemImag, hdcMemMask ;<br /> int x, y ;<br /> PAINTSTRUCT ps ;<br /> static HBRUSH hBrush;//定義一個畫刷,方便測試定義為靜態變數<br /> RECT rect;</p><p> switch (message)<br /> {<br /> case WM_CREATE:<br /> hInstance = ((LPCREATESTRUCT) lParam)->hInstance ;</p><p> // Load the original image and get its size</p><p> hBitmapImag = LoadBitmap (hInstance, TEXT ("Matthew")) ;<br /> hBrush=CreatePatternBrush(hBitmapImag);//利用原始位元影像建立位元影像畫刷<br /> GetObject (hBitmapImag, sizeof (BITMAP), &bitmap) ;<br /> cxBitmap = bitmap.bmWidth ;<br /> cyBitmap = bitmap.bmHeight ;</p><p> // Select the original image into a memory DC</p><p> hdcMemImag = CreateCompatibleDC (NULL) ;<br /> SelectObject (hdcMemImag, hBitmapImag) ;</p><p> // Create the monochrome mask bitmap and memory DC</p><p> hBitmapMask = CreateBitmap (cxBitmap, cyBitmap, 1, 1, NULL) ;<br /> hdcMemMask = CreateCompatibleDC (NULL) ;<br /> SelectObject (hdcMemMask, hBitmapMask) ;</p><p> // Color the mask bitmap black with a white ellipse</p><p> SelectObject (hdcMemMask, GetStockObject (BLACK_BRUSH)) ;<br /> Rectangle (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;<br /> SelectObject (hdcMemMask, GetStockObject (WHITE_BRUSH)) ;<br /> Ellipse (hdcMemMask, 0, 0, cxBitmap, cyBitmap) ;</p><p> // Mask the original image</p><p> BitBlt (hdcMemImag, 0, 0, cxBitmap, cyBitmap,<br /> hdcMemMask, 0, 0, SRCAND) ;//位元影像改變,位元影像畫刷改變不改變此時不曉得</p><p> DeleteDC (hdcMemImag) ;<br /> DeleteDC (hdcMemMask) ;<br /> return 0 ;</p><p> case WM_SIZE:<br /> cxClient = LOWORD (lParam) ;<br /> cyClient = HIWORD (lParam) ;<br /> return 0 ;</p><p> case WM_PAINT:<br /> hdc = BeginPaint (hwnd, &ps) ;<br /> rect.left=0;<br /> rect.top=0;<br /> rect.right=200;<br /> rect.bottom=320;<br /> Rectangle(hdc,rect.left,rect.top,rect.right,rect.bottom);<br />FillRect(hdc,&rect,hBrush);//用位元影像畫刷填充此地區,測試位元影像改變是否影響到了位元影像畫刷<br /> // Select bitmaps into memory DCs</p><p> hdcMemImag = CreateCompatibleDC (hdc) ;<br /> SelectObject (hdcMemImag, hBitmapImag) ;</p><p> hdcMemMask = CreateCompatibleDC (hdc) ;<br /> SelectObject (hdcMemMask, hBitmapMask) ;</p><p> // Center image</p><p> x = (cxClient - cxBitmap) / 2 ;<br /> y = (cyClient - cyBitmap) / 2 ;</p><p> // Do the bitblts</p><p> //BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326) ;<br /> //BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT) ;<br /> BitBlt (hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCCOPY) ;//證明位元影像是否改變<br /> DeleteDC (hdcMemImag) ;<br /> DeleteDC (hdcMemMask) ;<br /> EndPaint (hwnd, &ps) ;<br /> return 0 ;</p><p> case WM_DESTROY:<br /> DeleteObject (hBitmapImag) ;<br /> DeleteObject (hBitmapMask) ;<br /> DeleteObject(hBrush);//必須放到WM_DESTROY中,放在WM_PAINT中就會被刪除,下次將消失掉<br /> PostQuitMessage (0) ;<br /> return 0 ;<br /> }<br /> return DefWindowProc (hwnd, message, wParam, lParam) ;<br />}<br />
代碼中關鍵改變位置進行了注釋,需要資源檔可以藉助WINDOWS程式設計這本書中的源檔案,或者乾脆選自己一張長的比較清秀的圖片來測試吧。