1. 程式人生 > >14.4 GDI 點陣圖物件 (I)

14.4 GDI 點陣圖物件 (I)

摘錄於《Windows程式(第5版,珍藏版).CHarles.Petzold 著》P522

        在本章前面曾提到 Windows 從 1.0 版就開始支援 GDI 點陣圖物件。由於 Windows 3.0 引入了裝置無關點陣圖,因此 GDI 點陣圖物件現在有時也叫做裝置相關點陣圖(device-dependent bitmap)或 DDB。我將盡量不用裝置相關點陣圖的全稱,因為乍一看,這個詞和裝置無關點陣圖很像。它的縮寫 DDB 比較好,因為從視覺上更容易區別於 DIB。

        兩種不同點陣圖的存在給初次接觸 Windows 3.0 和以後版本的程式設計師帶來了許多迷惑。許多資深 Windows 程式設計師在準確理解 DIB 和 DDB 在某些方面有聯絡:DIB 可以被轉換成 DDB,反之亦然(儘管有些資訊會丟失)

。但是,DIB 和 DDB 並不能相互替代;並且要展現同樣的可視資料,也不是隨便在它們中選一個那麼簡單。

        如果我們可以假設 DIB 使 DDB 成為了過去式,那問題就變得方便容易多了,但事實並非如此。DDB 在 Windows 上仍然佔據著很重要的地位,尤其是在我們很在乎程式效能的時候

14.4.1  建立 DDB

        DDB 是 Windows 圖形裝置介面定義的若干圖形物件(包括畫筆、畫刷、字型、圖元檔案和調色盤)之一。這些圖形物件在 GDI 模組內部儲存,應用程式用數值控制代碼來引用它們。你可以用型別為 HBITMAP(“指向點陣圖的控制代碼”)的變數儲存一個指向 DDB 的控制代碼。例如,

HBITMAP hBitmap;
然後你可以通過呼叫 DDB 建立函式之一(如 CreateBitmap)來取得控制代碼。這些函式在 GDI 記憶體中分配並初始化一些記憶體以儲存關於點陣圖和實際點陣圖位的資訊。應用程式並不直接訪問此記憶體。此點陣圖獨立於任何裝置環境。當程式用完點陣圖後,應該刪除點陣圖:
DeleteObject (hBitmap);
如果在程式整個執行過程中都在使用 DDB,則可以在程式終止時用這種方法刪除點陣圖。

        CreateBitmap 函式的形式如下:

hBitmap = CreateBitmap(cx, cy, cPlanes, cBitsPixel, bits);
前兩個引數是點陣圖的寬度和高度,以畫素為單位。第 3 個引數是顏色平面的數量,第 4 個引數是每個畫素的位數。第 5 個引數指向一個對應於特定顏色格式的位陣列。如果不想用畫素位初始化 DDB,則可以把最後一個引數設成 NULL。畫素位可以以後再設定。

        使用這個函式時,Windows 允許你根據自己的需要建立任何 GDI 的奇怪的型別。比如,假設想要一個位圖,它有 7 個畫素寬,9 個畫素高,有 5 個顏色平面並且每個畫素有 3 位,則通過以下函式:

hBitmap = CreateBitmap(7, 9, 5, 3, NULL);
Windows 便會爽快地給你一個有效的點陣圖控制代碼。

        在這個函式呼叫期間,Windows 儲存你傳入函式的引數資訊,並且為畫素位分配記憶體。通過粗略計算,這個點陣圖需要 7 乘以 9 乘以 5 再乘以 3,也即 945 位,即需要 118 個位元組再加上一些零頭。

        然而,在 Windows 給點陣圖分配記憶體時,每個畫素行都有偶數個位元組。所以有如下程式碼:

iWidthBytes = 2 * ((cx * cBitsPixel + 15) / 16);
或者,C 程式設計師可能以如下這種格式來寫:
iWidthBytes = (cx * cBitsPixel + 15) & ~15) >> 3;
所以,分配給 DDB 的記憶體如下:
iBitmapBytes = cy * cPlanes * iWidthBytes;
在我們的例子中,iWidthBytes 是 4 個位元組,因此 iBitmapBytes 是 180 個位元組。

        現在, 點陣圖有 5 個顏色平面,每個畫素有 3 個顏色位,這意味著什麼?這其實並沒有很多含義,甚至都沒有實用價值。函式呼叫讓 GDI 分配一些內部記憶體,這個記憶體有特定的組織方式,但是這並不意味著什麼,你也沒法用這個點陣圖做任何有意義的工作。

        事實上,呼叫 CreateBitmap 函式時,你只能按照兩種方式設定引數:

  • cPlanes 和 cBitsPixel 值都等於 1(指明一個單色點陣圖)。
  • cPlanes 和 cBitsPixel 值等於某個裝置環境中的值,這個值可以通過用 PLANES 和 BITSPIXEL 索引呼叫 GetDeviceCaps 函式得到。

        在更加現實的情況下,將知識針對第一情況呼叫 CreateBitmap。對於第二種情況,可以用 CreateCompatibleBitmap 來化繁為簡:

hBitmap = CreateCompatibleBitmap (hdc, cx, cy);
該函式建立一個與裝置相容的點陣圖,該裝置的裝置環境控制代碼由第一個引數給出。CreateCompatibleBitmap 使用裝置環境控制代碼獲得 GetDeviceCaps 返回的資訊,然後把這些資訊傳給 CreateBitmap。除了和真實的裝置環境有相同的記憶體組織方式外,DDB 在其他方面和裝置環境無關。

        CreateDiscardableBitmp 函式有和 CreateCompatibleBitmap 同樣的引數,而且在功能上它們是相同的。在 Windows 早期版本中,CreateDiscardableBitmap 建立一個位圖,如果記憶體不多時,Windows 可以從記憶體中釋放此點陣圖。此後程式必須重新建立點陣圖

        第三個點陣圖建立函式是 CreateBitmapIndirect,如下所示:       

hBitmap = CreateBitmapIndirect (&bitmap);
其中 bitmap 是 BITMAP 型別的結構。BITMAP 結構定義如下:
typedef struct _tagBITMAP
{
    LONG      bmType;            // set to 0
    LONG      bmWidth;           // width in pixels
    LONG      bmHeight;          // height in pixels
    LONG      bmWidthBytes;      // width of row in bytes
    WORD      bmPlanes;          // number of color planes
    WORD      bmBitsPixel;       // number of bits per pixel
    LPVOID    bmBits;            // pointer to pixel bits
}
BITMAP, * PBITMAP;

        在呼叫 CreateBitmapIndirect 函式時,不需要設定 bmWidthBytes 欄位。Windows 會計算這個欄位值。還可以把 bmBits 欄位設為 NULL 或設為畫素位的地址來初始化點陣圖。

        BITMAP 結構也用在 GetObject 函式中。首先定義一個 BITMAP 型別的結構:

BITMAP bitmap;
然後如下呼叫該函式:
GetObject(hBitmap, sizeof(BITMAP), &bitmap);
Windows 會用點陣圖的有關資訊來填充 BITMAP 結構的欄位。但是,bmBits 欄位將會等於 NULL。

        最終應呼叫 DeleteObject 來銷燬在程式中建立的所有點陣圖。

14.4.2  點陣圖的位

        用 CreateBitmap 或 CreateBitmapIndirect 函式建立裝置相關 GDI 點陣圖物件時,可以給出指向點陣圖畫素位的指標。也可以不初始化點陣圖。Windows 提供了兩個能在點陣圖被建立後取得和設定點陣圖畫素位的函式。

        要設定畫素位,呼叫以下函式即可:

SetBitmapBits (hBitmap, cBytes, &bits);
GetBitmapBits 函式的語法形式也差不多,如下所示:
GetBitmapBits (hBitmap, cBytes, &bits);
在上面兩個函式裡,cBytes 代表要複製的位元組數,bits 是大小至少為 cBytes 的記憶體緩衝區。

        DDB 裡的畫素位從第一行開始排列。如前所述,每行有偶數個位元組。除了這一點,我沒什麼其他要說的。如果點陣圖時單色點陣圖,即點陣圖只有一個顏色平面,每個畫素只有一位,那麼每個畫素非 1 即 0。每行最左邊的畫素是這行裡第一個位元組的最高位。等我們搞清楚怎樣顯示 DDB 後,我們會在本章稍後部分建立一個單色 DDB。

        對於非單色點陣圖,你應該儘量避免需要指定畫素位具體值的情況。比如,假設 Windows 在 8 位 VGA 上執行。你呼叫了  CreateCompatibleBitmap 函式。通過 GetDeviceCaps 函式,你得知你在一個有一個顏色平面、每個畫素 8 位的裝置上執行程式。每個畫素用一個位元組儲存。但是,值為 0x37 的畫素意味著什麼呢?很明顯,它代表某種顏色,但這是什麼顏色呢?

        我們所說的畫素其實並代表某種特定的顏色。它只是一個值DDB 並沒有顏色表。本質的問題就是:當 DDB 顯示在螢幕上時,畫素是什麼顏色的?畫素必須表示某種顏色,但是究竟是什麼顏色呢?被顯示的畫素對應的是視訊卡的顏色查詢表中索引為 0x37 的 RGB 顏色這對於開發人員來說就是裝置相關

        然而,不要因為我們不知道畫素值表示什麼就認為非單色 DDB 一無是處。很快我們就會看到它們是多麼有用。在第 15 章,我們將看到 SetBitmapBits 和 GetBitmapBits 被更加有用的 SetDIBits 和 GetDIBits 函式取代。

        總之,基本的規則如下:對於彩色 DDB,不應該用 CreateBitmap 或 CreateBitmapIndirect 或 SetBitmapBits 來設定畫素位。只有對單色 DDB,才可以放心地設定畫素位。(如果通過呼叫 GetBitmapBits 取得了有同樣格式的另一個 DDB 的畫素位,那麼可以作為一種例外。)

        在繼續下面的介紹之前,我想提一下 SetBitmapDimensionEx 和 GetBitmapDimensionEx 函式。這些函式允許你設定(並取得)以 0.1 mm 為單位的點陣圖的度量尺寸。這個資訊和點陣圖定義一起儲存在 GDI 裡,但是不用於其他操作。這僅僅是一個標籤,讓你可以把度量尺寸和 DDB 聯絡起來。

14.4.3  記憶體裝置環境

        我們要探討的下一個概念是記憶體裝置環境(memory device context)。使用 GDI 點陣圖物件時,需要用到記憶體裝置環境

        通常,裝置環境對應於特定的圖形輸出裝置(比如視訊顯示或印表機)和裝置驅動。記憶體裝置環境只存在於記憶體。它不是一個真實的圖形輸出裝置,但是用大家的話說,它和特定的真實裝置“相容”。

        要建立一個記憶體裝置環境,必須有一個對應於真實裝置的裝置控制代碼。假設控制代碼是 hdc,則可以像下面這樣建立一個記憶體裝置環境:

hdcMem = CreateCompatibleDC (hdc);
通常這個函式呼叫比這條語句還要簡單。如果把引數設成 NULL,Windows 將創建於視訊顯示相容的記憶體裝置環境。應用程式建立任何記憶體裝置環境後,最終鍍硬鉻呼叫 DeleteDC 銷燬它。

        跟真實的點陣裝置一樣,記憶體裝置也有一個顯示錶面。然而,這個顯示錶面最開始很小——它是單色的,有 1 個畫素寬,1 個畫素高。顯示錶面僅僅是 1 位。

        當然,對於這個 1 位的顯示錶面,你做不了太多事。所以,有實際意義的下一步就是增大顯示錶面。為此,可以把 GDI 點陣圖物件選入記憶體裝置環境:

SelectObject (hdcMem, hBitmap);
向裝置環境中引入畫筆、畫刷、字型、區域和調色盤也是使用同樣的函式。但是,在各類裝置環境中,只有記憶體裝置環境才可以選入點陣圖。(如果有需要,也可以把其他 GDI 物件選進記憶體裝置環境。)

        在把點陣圖選進記憶體裝置環境時,SelectObject 僅僅在以下兩種情況才有效:點陣圖是單色點陣圖,或者此點陣圖與和記憶體裝置環境相容的裝置有同樣的顏色組織。這就是為什麼建立一個奇怪的 DDB(比如,點陣圖有 5 個顏色平面,每個畫素有 3 位)沒有什麼用的原因。

        通過前面的描述,我們知道,在呼叫 SelectObject 函式之後,DDB 成為記憶體裝置環境的顯示錶面。有了這個記憶體裝置環境,你可以做幾乎任何你可以在真實裝置環境上執行的操作。例如,如果用 GDI 繪圖函式在記憶體裝置環境上繪圖,影象就會畫在點陣圖上。這點很有用。也可以呼叫 BitBlt,把記憶體裝置環境作為源環境,視訊裝置環境作為目標環境。可以這樣在顯示器上畫點陣圖。另外,還可以把視訊裝置環境作為源環境,把記憶體裝置環境作為目標環境,呼叫 BitBlt,把影象從螢幕顯示到點陣圖上。我們將探討所有這些可能性。

14.4.4  載入點陣圖資源

        除了各種各樣的點陣圖建立函式,取得 GDI 點陣圖物件控制代碼的另一個方法是使用 LoadBitmap 函式有了這個函式,便無需擔心點陣圖格式。就像建立圖示或滑鼠指標一樣,只需要簡單建立一個位圖,並把它作為程式的一項資源即可。LoadBitmap 函式和 LoadIcon 及 LoadCursor 函式語法相同:

hBitmap = LoadBitmap(hInstance, szBitmapName);
如果想載入系統點陣圖,第一個引數可以設成 NULL。系統點陣圖是 Windows 可視介面的小部件,比如關閉框和選中標記,它們的識別符號以字母 OBM 開頭。如果點陣圖和某整數識別符號而不是名稱相關聯,則第二個引數可以使用 MAKEINTRESOURCE 巨集。所有用 LoadBitmap 載入的點陣圖最終應該用 DeleteObject 刪除。

        如果點陣圖資源是單色點陣圖,從 LoadBitmap 返回的控制代碼將代表單色點陣圖物件。如果點陣圖資源非單色,LoadBitmap 返回的控制代碼引用的則是 GDI 點陣圖物件,且此物件和程式正在其上執行的視訊顯示有相同的顏色組織。因此,該點陣圖總是與視訊顯示相容,並且可以被選進和視訊顯示相容的記憶體裝置環境。現在不必為 LoadBitmap 呼叫期間背後的顏色轉換而煩惱。我們會在第 15 章解釋顏色是怎樣轉換的。

        BRICKS1 程式展示了怎樣載入一個小的單色點陣圖資源。這個點陣圖本身看上去不完全像磚塊,但是如果橫向和縱向重複繪製這個點陣圖,看起來就好像一面磚牆。

/*------------------------------------------
	BRICKS1.C -- LoadBitmap Demonstration
				(c) Charles Petzold, 1998
------------------------------------------*/

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks1");
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("LoadBitmap Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static HBITMAP	hBitmap;
	static int		cxClient, cyClient, cxSource, cySource;
	BITMAP			bitmap;
	HDC				hdc, hdcMem;
	HINSTANCE		hInstance;
	int				x, y;
	PAINTSTRUCT		ps;

	switch (message)
	{
	case WM_CREATE:
		hInstance = ((LPCREATESTRUCT)lParam)->hInstance;

		hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));

		GetObject(hBitmap, sizeof(BITMAP), &bitmap);

		cxSource = bitmap.bmWidth;
		cySource = bitmap.bmHeight;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		hdcMem = CreateCompatibleDC(hdc);
		SelectObject(hdcMem, hBitmap);

		for (y = 0; y < cyClient; y += cySource)
			for (x = 0; x < cxClient; x += cxSource)
			{
				BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY);
			}

		ReleaseDC(hwnd, hdcMem);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
BRICKS1.RC (excerpts)

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

BRICKS                  BITMAP                  "Bricks.bmp"
BRICKS.BMP


        在 Visual C++ Developer Studio 中建立點陣圖時,指定點陣圖的高度和寬度為 8 個畫素,且為單色點陣圖,並給點陣圖取名“Bricks”。BRICKS1 程式在 WM_CREATE 訊息處理期間載入點陣圖,並且用 GetObject 確定它的畫素規模(這樣即使點陣圖不是 8 個畫素的正方形,程式仍然可以執行)。BRICKS1 程式稍後在 WM_DESTROY 訊息處理期間刪除了點陣圖控制代碼。

        處理 WM_PAINT 訊息時,BRICKS1 建立一個與顯示器相容的記憶體裝置環境,並且把點陣圖選進該環境。接下來就是從記憶體裝置環境到客戶區裝置環境的一系列 BitBlt 呼叫。之後程式刪除記憶體裝置環境。圖 14-6 下式了程式的執行結果。

圖 14-6  BRICKS1 的顯示

        順便提一下,Developer Studio 建立的 BRICKS.BMP 檔案是一個裝置無關點陣圖。你也可以在 Developer Studio 中試著建立一個彩色的 BRICKS.BMP 檔案(採用你選擇的任何一種顏色)並且確保一切順序。

        我們已經知道 DIB 可以被轉換成與視訊顯示相容的 GDI 點陣圖物件。第 15 章我們將介紹怎樣轉換。

14.4.5  單色點陣圖格式

        如果要處理小的單色影象,那麼並不一定要把它們建立成資源。與彩色點陣圖物件不同,單色點陣圖物件格式相對比較簡單,而且幾乎可以直接從你想要建立的影象匯出。例如,假設你想建立一個如下所示的點陣圖:

        你可以寫下一系列直接對應於網格的位(0 代表黑色,1 代表白色)。在從左到右讀這些位時,可以給每 8 位賦一個 16 進位制位元組。如果點陣圖的寬度不是 16 的倍數,在右邊添零以得到偶數個位元組

        這個點陣圖寬度為 20 個畫素,高度為 5 個掃描行,即寬度為 4 個位元組。可以用以下語句給這個點陣圖建立 BITMAP 結構:

static BITMAP bitmap = { 0, 20, 5, 4, 1, 1 };
並用一個 BYTE 陣列儲存這些位:
static BYTE bits [] = { 0x51, 0x77, 0x10, 0x00,
                        0x57, 0x77, 0x50, 0x00,
                        0x13, 0x77, 0x50, 0x00,                       
                        0x57, 0x77, 0x50, 0x00,
                        0x51, 0x11, 0x10, 0x00 };
用 CreateBitmapIndirect 函式建立這個點陣圖需要兩條語句:
bitmap.bmBits = (PSTR) bits;
hBitmap = CreateBitmapIndirect (&bitmap);
另一個方法是:
hBitmap = CreateBitmapIndirect (&bitmap);
SetBitmapBits (hBitmap, sizeof(bits), bits);
也可以用一條語句建立這個點陣圖:
hBitmap = CreateBitmap(20, 5, 1, 1, bits);

        BRICKS2 程式用上述技術直接建立了磚塊點陣圖,而沒有用到資原始檔。

/*------------------------------------------
	BRICKS2.C -- CreateBitmap Demonstration
		    (c) Charles Petzold, 1998
------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks2");
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("CreateBitmap Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static BITMAP	bitmap = { 0, 8, 8, 2, 1, 1 };
	static BYTE		bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
								   0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };

	static HBITMAP	hBitmap;
	static int		cxClient, cyClient, cxSource, cySource;
	HDC				hdc, hdcMem;
	int				x, y;
	PAINTSTRUCT		ps;

	switch (message)
	{
	case WM_CREATE:
		bitmap.bmBits = bits;
		hBitmap = CreateBitmapIndirect(&bitmap);
		cxSource = bitmap.bmWidth;
		cySource = bitmap.bmHeight;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		hdcMem = CreateCompatibleDC(hdc);
		SelectObject(hdcMem, hBitmap);

		for (y = 0; y < cyClient; y += cySource)
			for (x = 0; x < cxClient; x += cxSource)
			{
				BitBlt(hdc, x, y, cxSource, cySource, hdcMem, 0, 0, SRCCOPY);
			}

		DeleteDC(hdcMem);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

        你或許想嘗試用類似方法建立彩色點陣圖。比如,如果你的視訊顯示在 256 色模式下執行,你可以用本章前面的色彩定義表格對每個彩色磚塊的畫素進行定義。但是,這段程式在其他任何視訊模式下都將無法正確執行。以裝置無關的方式處理彩色點陣圖需要用到第 15 章討論的 DIB。

14.4.6  點陣圖畫刷

        BRICKS 系列的最後一個程式是 BRICKS3。乍一看這個程式,你可能會有這樣的反應:“和點陣圖有關的程式碼在哪裡?”

/*---------------------------------------------------
	BRICKS3.C -- CreatePatternBrush Demonstration
		    (c) Charles Petzold, 1998
---------------------------------------------------*/

#include <windows.h>
#include "resource.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevinstance,
	PSTR szCmdLine, int iCmdShow)
{
	static TCHAR szAppName[] = TEXT("Bricks3");
	HBITMAP		 hBitmap;
	HBRUSH		 hBrush;
	HWND		 hwnd;
	MSG			 msg;
	WNDCLASS	 wndclass;

	hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));
	hBrush = CreatePatternBrush(hBitmap);
	DeleteObject(hBitmap);

	wndclass.style = CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = 0;
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(NULL, IDI_INFORMATION);
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = hBrush;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires Windows NT!"),
			szAppName, MB_ICONERROR);

		return 0;
	}

	hwnd = CreateWindow(szAppName, TEXT("CreatePatternBrush Demo"),
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL, NULL, hInstance, NULL);

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message)
	{
	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}
BRICKS3.RC (excerpts)

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//

BRICKS                  BITMAP                  "Bricks.bmp"

        這段程式和 BRICKS1 使用了相同的 BRICKS.BMP,而且視窗看起來也一樣。

        可以看出,視窗過程沒做什麼。BRICKS3 實際上把磚塊圖案作為視窗類的背景畫刷來使用,背景畫刷在 WNDCLASS 結構的 hbrBackground 欄位定義。

        正如你現在猜想的一樣,GDI 畫刷是很小的點陣圖,通常為 8 * 8 畫素的正方形。要把一個位圖變成畫刷,可以呼叫 CreatePatternBrush,或者呼叫 CreateBrushIndirect 並把 LOGBRUSH 結構的 lbStyle 欄位設定成 BS_PATTERN。點陣圖至少要是 8 畫素寬,8 畫素高。如果更大,Windows 98 只取圖的左上角作為畫刷。而 Windows NT 則沒有這個限制,它可以把整個點陣圖都變成畫刷。

        記住,畫刷和點陣圖都是 GDI 物件,所以應該在程式終止前刪除任何建立的畫刷和點陣圖。基於點陣圖建立畫刷時,Windows 會複製一份點陣圖的位資訊,供畫刷繪圖時使用。在呼叫 CreatePatternBrush(或者 CreateBrushIndirect)之後,可以立即刪除點陣圖,這不會影響畫刷。依次類推,還可以刪除畫刷,而不影響最初用來建立畫刷的點陣圖。請注意,BRICKS3 在建立畫刷後刪除了點陣圖,而且在程式終止前刪除了畫刷。

相關推薦

14.4 GDI 點陣物件 (I)

摘錄於《Windows程式(第5版,珍藏版).CHarles.Petzold 著》P522         在本章前面曾提到 Windows 從 1.0 版就開始支援 GDI 點陣圖物件。由於 Windows 3.0 引入了裝置無關點陣圖,因此 GDI 點陣圖物件現在有時也

如何將點陣物件儲存為BMP檔案

  GDI中點陣圖物件是很常見的GDI物件,但是無論是SDK,還是MFC都沒有提供現在的函式或是方法來將一個位圖物件儲存為一個BMP檔案,這裡介紹一下儲存方法。 點陣圖檔案格式: DIB檔案有四個主要部分: 檔案表頭(BITMAPFILEHEADER) 資訊表頭  (BITM

GDI+ 在Delphi程式的應用 -- GDI+影象與GDI點陣的相互轉換

        Delphi的TBitmap封裝了Windows的GDI點陣圖,因此,TBitmap只支援bmp格式的影象,但是在Delphi應用程式中,常常會遇到圖形格式的轉換,如將Delphi點陣圖TBitmap的影象轉換為其它格式儲存,或者將其它影象格式轉換為TBit

使用gdal讀取影象資料,然後用構建gdi+點陣顯示

如果想利用雙快取顯示gdal讀取的影象資料,把影象資料構建成一個gdi+的點陣圖來顯示void CGdalGdiView::OnDraw(CDC* /*pDC*/) { CGdalGdiDoc* pDoc = GetDocument(); ASSERT_VALID(pD

使用GDI+點陣資料掃描線處理影象的小技巧

   在GDI+影象處理中,我們經常利用BitmapData結構對影象資料掃描線進行操作,在我的大部分BOLG文章中,都使用了這個方法。GDI+點陣圖通過其LockBits方法和UnlockBits方法,分別用來鎖定(獲取)和解鎖(釋放)BitmapData資料,我們一般

三行程式碼實現gdi+ Bitmap物件匯入資原始檔點陣

下午在網上找了很久關於把資原始檔中的點陣圖匯入到gdi+的Bitmap物件中的方法,但是網上的方法不是要寫大段的程式碼,就是根本無法實現預期目標。最後通過我自己的嘗試發現了一個簡單易用的方法。就是先把點陣圖匯入到一個gdi物件CBitmap中然後通過其控制代碼載入到gdi+的

使用GDI+實現24 點陣轉32位點陣

  今天利用修改影象的alpha通道實現了影象的倒影,但是在對影象進行測試的時候,發現24位的點陣圖不能實現倒影,究其原因是24位以下的影象沒有alpha通道,也就沒辦法利用修改alpha通道的方式去實現影象的倒影。於是就想辦法實現24點陣圖像轉成32點陣圖像。也就能具有al

VC下2、4、8、16、24、32位點陣的資料解析與顯示

在VC中,點陣圖顯示一般有現成的方式,如使用picture控制元件、GetDC()->StretchBlt、::BitBlt等,但這些方式都是高層的封裝,讓你不清楚一副點陣圖是如何解析並顯示到DC上的。實際應用中,比如影象處理,視訊顯示等,需要操作到點陣圖中的畫素,這

使用GDI+讀取常用點陣格式

ATL類CImage(在atlimage.h)已經封裝了GDI+中隊點陣圖的常用操作,通過msdn可以看到這樣的描述“CImage provides enhanced bitmap support, including the ability to load and save

Linux檔案系統分析之二(超級塊,i節點點陣和邏輯塊點陣)

第二個扇區和第一個扇區一樣屬於引導塊,這裡就不列舉出其內容了,這裡的一塊是兩個扇區即1024B。接下來的一塊就是大名鼎鼎的超級塊了。其內容如下:00000400h: E0 01 A0 05 01 00 01 00 13 00 00 00 00 1C 08 10 ; ??..

24位點陣4位彩色(BMP)

之前的“24位點陣圖轉4位灰度圖”中已經說明了,調色盤與圖象資料格式。 這裡對圖象資料格式做下補充,並講解24位點陣圖轉4位彩色圖的演算法 1.圖象資料格式 在我完成這個演算法的編碼時,執行效果有一個非常嚴重的錯誤,就是所有的藍和紅色反了。也就是說,應該是藍色的地方呈現了紅色,應該是紅色的地方呈現了蘭色。

點陣4位元組對齊問題

在自己對影象資料進行處理的時候,會有位元組對其的問題,由於之前使用的影象大都是8bit或者是24bit,32bit的影象,使用的對其公式是(pixelwidth*channel+3)/4*4。後面也有看到有些寫法如:(width * bitCounts + 31) / 32

redis 4 點陣Bitmaps

一般知道前面介紹的五種redis資料結構,就可以開心的玩耍了,但如果知道Bitmaps,Hyperloglogs,GEO,就更開心了。 這次我們來看下Bitmaps。 簡介 假設一個場景:記錄使用者的簽到天數。 方法一:將使用者的id和日期關聯起

GDI之繪製點陣

#include <Windows.h> #include "resource.h" #include <wingdi.h> #pragma comment(lib,"msimg32.lib") // 視窗處理函式 HINSTANCE g_hIns

自己定義View之Chart標系列(1)——點陣

tint 發現 坐標軸 畫的 tracking androi mit def dot 近期要做一些圖表類的需求,一開始就去github上看了看,發現開源的圖表框架還是蠻多的。可是非常少有全然符合我的需求的。另外就是使用起來比較麻煩。所以就決定自己來造輪子

Ubunte 14.4快捷鍵使用

命令ctrl+alt+f1.....f5 進入命令模式ALT+F7 返回桌面模式ctrl+alt+t 調出超級終端窗口Ubunte 14.4快捷鍵使用

PCB (4)原理導入PCB

ima mage body 導入 原理圖 bsp 原理 div http 2 自動對齊 PCB (4)原理圖導入PCB

Ubuntu 14.4 安裝OpenVZ

passwd amd64 _for grub code 文章 cgroup down 32位系統 添加源 vim /etc/apt/sources.list.d/openvz.list 寫入下面內容保存 如果需要,可以視情況改動註釋..(如果看不懂,請不要在意這行字) de

java9新特性-14-多分辨率像 API

.com images idp 矽谷 ans clas 屏幕分辨率 tps 面試 1.官方Feature 251: Multi-Resolution Images 263: HiDPI Graphics on Windows and Linux 2.產生背景 在Ma

14.4 exportfs命令 14.5 NFS客戶端問題 15.1 FTP介紹

使用vsftpd搭建ftp14.4 exportfs命令 客戶端: 14.5 NFS客戶端問題 mount -t nfs -o nfsvers=3 192.168.15.132:/tmp /mnt/nfs 15.1 FTP介紹 15.2/15.3 使用vsftpd搭建ftp 14.4 export