Windows程序設計核心總結(GDI位圖對象-2018.5.8)

DDB:設備相關位圖,屬於GDI對象,跟畫筆、畫刷同樣在GDI模塊內部存儲。
HBITMAP hBitmap;//存儲一個指向DDB的句柄windows

1.建立DDB

(1)hBitmap=CreateBitmap(cx,cy,cPlanes,cBitsPixel,bits)
/
該函數是第一個建立GDI位圖對象,也叫設備相關位圖(DDB)。
該函數的第3、四個參數的設置有兩種狀況:
1.cPlanes,cBitsPixel都爲1,表明該位圖是單色位圖。
2.cPlanes,cBitsPixel值等於某個設備環境中的值,經過PLANES和BITSPIXEL索引調用GetDeviceCaps函數獲取這兩個參數的值。
/數組

(2)hBitmap=CreateCompatibleBitmap(hdc,cx,cy)
/*
該函數建立一個與設備兼容的GDI位圖對象。
什麼是設備兼容?其實就是位圖和真實的設備(視頻顯示器)有一樣的顏色組織(cPlanes,cBitsPixel)。爲何要與設備兼容?你想一想,若是我建立的位圖的顏色組織和設備的顏色組織不同,那麼有可能位圖想顯示的顏色在設備中可能沒法顯示出來,那不就出錯了。ide

該函數使用參數1指定的設備環境句柄得到GetDeviceCaps返回的信息,而後把這些信息傳給CreateBitmap,再返回GDI位圖對象句柄。
*/函數

(3)hBitmap=CreateBitmapIndirect(&bitmap)
/*
參數bitmap是BITMAP類型的結構,該類型結構定義以下:測試

typedef struct tagBITMAP {
  LONG   bmType;//位圖類型,該字段必須爲0
  LONG   bmWidth;//橫向像素個數
  LONG   bmHeight;//縱向像素個數
  LONG   bmWidthBytes;//不須要設置,Windows會自動計算這個字段的值
  WORD   bmPlanes;//顏色平面數
  WORD   bmBitsPixel;//每一個像素的顏色位數
  LPVOID bmBits;//設爲NULL或某個帶有值的字節類型數組的地址來初始化位圖的全部像素位
} BITMAP, *PBITMAP;

能夠利用GetObject函數獲取指定位圖對象的信息。例如:
BITMAP bitmap;
GetObject(hBitmap,sizeof(BITMAP),&bitmap);
//參數1:指定須要獲取的GDI位圖對象
*/字體

總結:這些函數都可以建立GDI位圖對象,而後Windows爲這些位圖對象在內存中分配內存並初始化,以及存儲位圖對象的信息,例如:橫縱像素數、顏色平面數、每一個像素的顏色位數和每一個像素的顏色值。注意,當建立GDI位圖對象並使用完該對象後,必須調用DeleteObject函數刪除在程序中建立的全部位圖。ui

2.位圖的像素位問題

咱們都知道一個位圖由許多矩形的像素組成,每一個像素的顏色位數要麼1位(單色位圖)、要麼多位(彩色位圖),那麼每一個像素有着顏色值。Windows提供了兩個函數能在位圖被建立後設置或獲取位圖的全部像素位。
1.SetBitmapBits(hBitmap,cBytes,&bits);
2.GetBitmapBits(hBitmap,cBytes,&bits);spa

言歸正傳,假設某個DDB(每一個像素8個顏色位)的某個像素的顏色值爲0x37,這表明什麼顏色。其實,DDB沒有顏色表,但當這個DDB顯示在屏幕上時,被顯示的這個像素對應的是視頻卡的顏色查找表中索引爲0x37的RGB顏色,因此這就叫設備相關。位圖中的像素的顏色與設備相關,因此這就叫設備相關性。分析到這裏咱們就能夠想到,若是咱們不知道視頻卡的顏色查找表對應的顏色,那麼咱們在建立DDB時,能夠自定義的初始化位圖的全部像素位嗎?不,這是不能夠的。因此,Windows有了這樣的規則:對於彩色DDB,不該該調用CreateBitmap或CreateBitmapIndirect或SetBitmapBits函數來設置位圖的全部像素位,只有對單色DDB,才能夠放心地設置像素位,由於單色只有0或1,誰都知道。指針

3.內存設備環境

咱們建立了GDI位圖對象(DDB)後,使用GDI位圖對象就要用到內存設備環境,由於只有內存設備環境才能夠選入位圖,其餘種類的設備環境都不能夠。而內存設備環境卻能夠選入其餘GDI對象(畫筆、畫刷、字體、調色板和區域)。通常地設備環境都是對應於特定的圖形輸出設備(視頻顯示器或打印機),而內存設備環境只存在於內存,它不是真實的圖形輸出設備,但它卻和這些真實的設備「兼容」,意思是說內存設備環境和真實設備的設備環境的顏色組織相同。爲了能實現內存設備環境和真實的設備「兼容」,那麼在建立內存設備環境時,要有一個對應於真實設備的設備句柄,例如:
hdcMem=CreateCompatibleDC(hdc);//若是參數設爲NULL,那麼Windows將建立與視頻顯示器兼容的內存設備環境。code

真實的設備,例如:視頻顯示器,都是一個點陣設備,而內存設備環境也有一個顯示錶面,然而這個顯示錶面只有1個像素寬,1個像素高,單色的。因此,若是要在內存設備環境搞事情,必須得增大顯示錶面,爲此,能夠把剛剛建立的GDI位圖對象選入內存設備環境。例如:
SelectObject(hdcMem,hBitmap);

注意:SelectObject函數調用是否有效,取決於兩種狀況:
(1)位圖是單色位圖。
(2)位圖與內存設備環境兼容的真實設備有一樣的顏色組織。意思是說,在建立GDI位圖對象時,只有位圖和真實設備兼容,SelectObject函數纔會有效。
一句話歸納:若是位圖是單×××,暢通無阻,能夠直接選入內存設備環境。建立彩色位圖須要兼容真實設備,而建立內存設備環境須要兼容真實設備,那麼調用SelectObject函數將位圖選入內存設備環境纔會成功。

咱們在內存設備環境中能夠像在真實的設備環境中作任何事,例如:畫圖等。注意,繪圖操做是在位圖上繪畫,本來的位圖被新的位圖覆蓋了。

4.加載位圖資源

除了利用位圖建立函數來建立位圖對象,也能夠經過加載位圖資源,這種方法能夠保證的是加載進來的位圖對象和視頻顯示器有着相同的顏色組織,即與視頻顯示器兼容。
例如:
hBitmap=LoadBitmap(hInstance,szBitmapName);
/
這個函數的使用跟加載圖標、鼠標指針同樣,但值得注意的是:使用系統位圖時指定系統預設的標識符會報錯,須要在windows.h頭文件前添加#define OEMRESOURCE。
/

全部經過LoadBitmap函數加載的位圖對象,最後都應該用DeleteObject函數刪除。

代碼例子:

#define OEMRESOURCE
#include<windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Bricks1");
    HWND hWnd;//窗口句柄
    MSG mSg;//消息結構體
    //建立窗口類
    WNDCLASSEX wndClass;

    //設置窗口類各種屬性
    wndClass.cbSize = sizeof(WNDCLASSEX);//設置窗口類結構體大小
    wndClass.cbClsExtra = 0;//窗口類尾部的一部分額外的空間
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;//應用程序當前的實例句柄
    wndClass.hCursor = LoadCursor(NULL, IDC_HELP);
    wndClass.hIcon = NULL;
    wndClass.hIconSm = NULL;
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpfnWndProc = WndProc;//回調函數的地址(窗口消息處理程序)
    wndClass.lpszClassName = szAppName;//窗口類的名字,也就是窗口的標識,後面用於建立窗口函數的參數。
    wndClass.lpszMenuName = NULL;//菜單的名字,沒有爲NULL。
    wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口類的樣式,它的值能夠是窗口樣式值的任意組合。CS_HREDRAW  CS_VREDRAW,這個是垂直刷新和水平刷新,窗口尺寸改變,重畫活動區域。

    //註冊對話框類
    if (!RegisterClassEx(&wndClass))
    {
        DWORD error_code = GetLastError();
        MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL);
        return 0;
    }

    hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&mSg, NULL, 0, 0))
    {
        TranslateMessage(&mSg);
        DispatchMessage(&mSg);
    }
    return (int)mSg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps;
    int x, y;
    static int cxClient, cyClient,cxSource,cySource;
    static HBITMAP hBitmap;
    HDC hdc, hdcMem;
    HINSTANCE hInstance; 
    BITMAP bitmap;
    switch (message)
    {
    case WM_CREATE:
        //獲取應用程序實例句柄
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
        /*
        加載位圖資源,該位圖是一個8*8的單色位圖
        除了加載自定義的位圖資源,也能夠加載系統位圖資源。
        例如:
            hBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_DNARROW));//注意,OBM開頭的標識符會提示不存在,須要在windows.h頭文件前添加#define OEMRESOURCE
        */
        hBitmap = LoadBitmap(NULL, MAKEINTRESOURCE(OBM_DNARROW));
        //hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));
        //獲取加載GDI位圖對象的信息
        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);
            }
        }
        DeleteDC(hdcMem);
        EndPaint(hwnd, &ps);
        return 0;
    case WM_CLOSE:
        if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("對話框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION))
        {
            DestroyWindow(hwnd);
        }
        else
        {
            return 0;
        }
    case WM_DESTROY:
        DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

5.單色位圖格式

由前面可知,Windows提供了一個規則:對於彩色DDB,不該該調用CreateBitmap或CreateBitmapIndirect或SetBitmapBits函數來設置位圖的全部像素位,只有對單色DDB,才能夠放心地設置像素位,由於單色只有0或1,誰都知道。因此,若是位圖只是單色位圖,那我就能夠本身手動建立一個位圖對象,並用一個字節數組初始化位圖的全部像素位,由於像素位都是1位,1(白色)或者0(黑色)而已。

代碼例子:
/
下面的代碼是在客戶區重複顯示內存設備環境的位圖,造成一個真正的磚牆啦!
/

#include<windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Bricks2");
    HWND hWnd;//窗口句柄
    MSG mSg;//消息結構體
    //建立窗口類
    WNDCLASSEX wndClass;

    //設置窗口類各種屬性
    wndClass.cbSize = sizeof(WNDCLASSEX);//設置窗口類結構體大小
    wndClass.cbClsExtra = 0;//窗口類尾部的一部分額外的空間
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;//應用程序當前的實例句柄
    wndClass.hCursor = LoadCursor(NULL, IDC_HELP);
    wndClass.hIcon = NULL;
    wndClass.hIconSm = NULL;
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpfnWndProc = WndProc;//回調函數的地址(窗口消息處理程序)
    wndClass.lpszClassName = szAppName;//窗口類的名字,也就是窗口的標識,後面用於建立窗口函數的參數。
    wndClass.lpszMenuName = NULL;//菜單的名字,沒有爲NULL。
    wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口類的樣式,它的值能夠是窗口樣式值的任意組合。CS_HREDRAW  CS_VREDRAW,這個是垂直刷新和水平刷新,窗口尺寸改變,重畫活動區域。

    //註冊對話框類
    if (!RegisterClassEx(&wndClass))
    {
        DWORD error_code = GetLastError();
        MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL);
        return 0;
    }

    hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&mSg, NULL, 0, 0))
    {
        TranslateMessage(&mSg);
        DispatchMessage(&mSg);
    }
    return (int)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 };
    PAINTSTRUCT ps;
    int x, y;
    static int cxClient, cyClient,cxSource,cySource;
    static HBITMAP hBitmap;
    HDC hdc, hdcMem;
    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_CLOSE:
        if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("對話框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION))
        {
            DestroyWindow(hwnd);
        }
        else
        {
            return 0;
        }
    case WM_DESTROY:
        DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

6.位圖畫刷

呃,這個其實沒什麼好說,就是將位圖對象添加進畫刷,而後用這個畫刷繪製客戶區背景而已。

代碼例子:

#include<windows.h>
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Bricks2");
    HWND hWnd;//窗口句柄
    MSG mSg;//消息結構體
    //建立窗口類
    WNDCLASSEX wndClass;
    HBITMAP hBitmap;
    HBRUSH hBrush;
    hBitmap = LoadBitmap(hInstance, TEXT("Bricks"));
    hBrush = CreatePatternBrush(hBitmap);
    DeleteObject(hBitmap);

    //設置窗口類各種屬性
    wndClass.cbSize = sizeof(WNDCLASSEX);//設置窗口類結構體大小
    wndClass.cbClsExtra = 0;//窗口類尾部的一部分額外的空間
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;//應用程序當前的實例句柄
    wndClass.hCursor = LoadCursor(NULL, IDC_HELP);
    wndClass.hIcon = NULL;
    wndClass.hIconSm = NULL;
    wndClass.hbrBackground = hBrush;
    wndClass.lpfnWndProc = WndProc;//回調函數的地址(窗口消息處理程序)
    wndClass.lpszClassName = szAppName;//窗口類的名字,也就是窗口的標識,後面用於建立窗口函數的參數。
    wndClass.lpszMenuName = NULL;//菜單的名字,沒有爲NULL。
    wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口類的樣式,它的值能夠是窗口樣式值的任意組合。CS_HREDRAW  CS_VREDRAW,這個是垂直刷新和水平刷新,窗口尺寸改變,重畫活動區域。

    //註冊對話框類
    if (!RegisterClassEx(&wndClass))
    {
        DWORD error_code = GetLastError();
        MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL);
        return 0;
    }

    hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&mSg, NULL, 0, 0))
    {
        TranslateMessage(&mSg);
        DispatchMessage(&mSg);
    }
    return (int)mSg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CLOSE:
        if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("對話框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION))
        {
            DestroyWindow(hwnd);
        }
        else
        {
            return 0;
        }
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

7.在位圖上繪圖

先建立一個位圖對象,再將位圖對象選入內存設備環境,而後在內存設備環境繪圖,那麼本來的位圖就被繪圖後的位圖給覆蓋了。接着調用BitBlt或者StretchBlt函數將內存設備環境的位圖複製進客戶區設備環境。

#include<windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("HelloBit");
    HWND hWnd;//窗口句柄
    MSG mSg;//消息結構體
    //建立窗口類
    WNDCLASSEX wndClass;

    //設置窗口類各種屬性
    wndClass.cbSize = sizeof(WNDCLASSEX);//設置窗口類結構體大小
    wndClass.cbClsExtra = 0;//窗口類尾部的一部分額外的空間
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;//應用程序當前的實例句柄
    wndClass.hCursor = LoadCursor(NULL, IDC_HELP);
    wndClass.hIcon = NULL;
    wndClass.hIconSm = NULL;
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpfnWndProc = WndProc;//回調函數的地址(窗口消息處理程序)
    wndClass.lpszClassName = szAppName;//窗口類的名字,也就是窗口的標識,後面用於建立窗口函數的參數。
    wndClass.lpszMenuName = szAppName;//菜單的名字,沒有爲NULL。
    wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口類的樣式,它的值能夠是窗口樣式值的任意組合。CS_HREDRAW  CS_VREDRAW,這個是垂直刷新和水平刷新,窗口尺寸改變,重畫活動區域。

    //註冊對話框類
    if (!RegisterClassEx(&wndClass))
    {
        DWORD error_code = GetLastError();
        MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL);
        return 0;
    }

    hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&mSg, NULL, 0, 0))
    {
        TranslateMessage(&mSg);
        DispatchMessage(&mSg);
    }
    return (int)mSg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HBITMAP hBitmap;
    static HDC hdcMem;
    static int cxBitmap, cyBitmap, cxClient, cyClient, iSize = ID_SIZE_BIG;
    static TCHAR *szText = TEXT("Hello,world!");
    HDC hdc;
    HMENU hMenu;
    int x, y;
    PAINTSTRUCT ps;
    SIZE size;
    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        hdcMem = CreateCompatibleDC(hdc);
        //獲取文本字符串的橫縱像素數
        GetTextExtentPoint32(hdc, szText, lstrlen(szText), &size);
        cxBitmap = size.cx;
        cyBitmap = size.cy;
        //建立和szText字符串同樣寬度與高度的兼容性GDI位圖對象
        hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);

        ReleaseDC(hwnd, hdc);
        SelectObject(hdcMem, hBitmap);
        TextOut(hdcMem, 0, 0, szText, lstrlen(szText));
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_COMMAND:
        hMenu = GetMenu(hwnd);
        switch (LOWORD(wParam))
        {
        case ID_SIZE_BIG:
        case ID_SIZE_SMALL:
            //在選擇菜單項以前,初始時是選中ID_SIZE_BIG的
            CheckMenuItem(hMenu, iSize, MF_UNCHECKED);
            iSize = LOWORD(wParam);
            CheckMenuItem(hMenu, iSize, MF_CHECKED);
            InvalidateRect(hwnd, NULL, TRUE);
            break;
        }
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);//開始繪製窗口
        switch (iSize)
        {
        case ID_SIZE_BIG:
            StretchBlt(hdc, 0, 0, cxClient, cyClient, hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY);
            break;
        case ID_SIZE_SMALL:
            for (y = 0; y < cyClient; y += cyBitmap)
            {
                for (x = 0; x < cxClient; x += cxBitmap)
                {
                    //將內存設備環境的位圖對象傳輸到整個客戶區,看上去像牆同樣
                    BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY);
                }
            }
        }
        EndPaint(hwnd, &ps);
        return 0;
    case WM_CLOSE:
        if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("對話框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION))
        {
            DestroyWindow(hwnd);
        }
        else
        {
            return 0;
        }
    case WM_DESTROY:
        DeleteDC(hdcMem);
        DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

8.陰影位圖

按下鼠標左鍵而後拖動鼠標,就用黑色畫筆繪畫,按下鼠標右鍵而後拖動鼠標,就用白色畫筆繪畫。值得注意的是,咱們在代碼例子裏調用了SetCapture(hwnd),說明當鼠標在客戶區外,也能接收到鼠標消息,咱們能夠這樣作個小測試,先註釋掉WM_PAINT消息處理中的BitBlt(hdc, 0,0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);,發現沒有,若是按下鼠標左鍵並拖動鼠標,當拖動到客戶區外,其實在內存設備環境裏已經保存了在客戶區外繪製的圖,由於內存設備環境的尺寸是依據客戶區放最大後的尺寸,而後咱們改變窗口大小,客戶區啥都沒有了,這是由於沒有繼續從內存設備環境複製保存的位圖到客戶區設備環境上。

代碼例子:

#include<windows.h>
#include<windowsx.h>
#include "resource.h"
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdline, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Sketch");
    HWND hWnd;//窗口句柄
    MSG mSg;//消息結構體
    //建立窗口類
    WNDCLASSEX wndClass;

    //設置窗口類各種屬性
    wndClass.cbSize = sizeof(WNDCLASSEX);//設置窗口類結構體大小
    wndClass.cbClsExtra = 0;//窗口類尾部的一部分額外的空間
    wndClass.cbWndExtra = 0;
    wndClass.hInstance = hInstance;//應用程序當前的實例句柄
    wndClass.hCursor = LoadCursor(NULL, IDC_HELP);
    wndClass.hIcon = NULL;
    wndClass.hIconSm = NULL;
    wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndClass.lpfnWndProc = WndProc;//回調函數的地址(窗口消息處理程序)
    wndClass.lpszClassName = szAppName;//窗口類的名字,也就是窗口的標識,後面用於建立窗口函數的參數。
    wndClass.lpszMenuName = NULL;//菜單的名字,沒有爲NULL。
    wndClass.style = CS_HREDRAW | CS_VREDRAW;//窗口類的樣式,它的值能夠是窗口樣式值的任意組合。CS_HREDRAW  CS_VREDRAW,這個是垂直刷新和水平刷新,窗口尺寸改變,重畫活動區域。

    //註冊對話框類
    if (!RegisterClassEx(&wndClass))
    {
        DWORD error_code = GetLastError();
        MessageBox(NULL, TEXT("This program requires Windows NT!"), TEXT("NumRain"), MB_ICONERROR | MB_OKCANCEL);
        return 0;
    }

    hWnd = CreateWindow(szAppName, TEXT("The Hello Program"), WS_OVERLAPPEDWINDOW, 200, 200, 800, 500, NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, iCmdShow);
    UpdateWindow(hWnd);
    while (GetMessage(&mSg, NULL, 0, 0))
    {
        TranslateMessage(&mSg);
        DispatchMessage(&mSg);
    }
    return (int)mSg.wParam;
}

void GetLargestDisplayMode(int *pcxBitmap, int *pcyBitmap)
{
    DEVMODE devmode;
    int iModeNum = 0;
    *pcxBitmap = *pcyBitmap = 0;

    ZeroMemory(&devmode, sizeof(DEVMODE));
    devmode.dmSize = sizeof(DEVMODE);

    while (EnumDisplaySettings(NULL, iModeNum++, &devmode))
    {
        *pcxBitmap = max(*pcxBitmap, (int)devmode.dmPelsWidth);
        *pcyBitmap = max(*pcyBitmap, (int)devmode.dmPelsHeight);
    }
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL fLeftButtonDown, fRightButtonDown;
    static HBITMAP hBitmap;
    static HDC hdcMem;
    static int cxBitmap, cyBitmap, cxClient, cyClient, xMouse,yMouse;
    HDC hdc;
    PAINTSTRUCT ps;
    switch (message)
    {
    case WM_CREATE:
        GetLargestDisplayMode(&cxBitmap, &cyBitmap);
        hdc = GetDC(hwnd);
        hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
        hdcMem = CreateCompatibleDC(hdc);
        ReleaseDC(hwnd, hdc);
        if (!hBitmap)
        {
            DeleteDC(hdcMem);
            return -1;
        }
        SelectObject(hdcMem, hBitmap);
        PatBlt(hdcMem, 0, 0, cxBitmap, cyBitmap, WHITENESS);
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_LBUTTONDOWN:
        if (!fRightButtonDown)
        {
            SetCapture(hwnd);
        }
        xMouse = GET_X_LPARAM(lParam);
        yMouse = GET_Y_LPARAM(lParam);
        fLeftButtonDown = TRUE;
        return 0;
    case WM_LBUTTONUP:
        if (fLeftButtonDown)
        {
            SetCapture(NULL);
        }
        fLeftButtonDown = FALSE;
        return 0;
    case WM_RBUTTONDOWN:
        if (!fLeftButtonDown)
        {
            SetCapture(hwnd);
        }
        xMouse = GET_X_LPARAM(lParam);
        yMouse = GET_Y_LPARAM(lParam);
        fRightButtonDown = TRUE;
        return 0;
    case WM_RBUTTONUP:
        if (fRightButtonDown)
        {
            SetCapture(NULL);
        }
        fRightButtonDown = FALSE;
        return 0;
    case WM_MOUSEMOVE:
        if (!fLeftButtonDown&&!fRightButtonDown)
        {
            return 0;
        }
        hdc = GetDC(hwnd);
        SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
        SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));

        MoveToEx(hdc, xMouse, yMouse, NULL);
        MoveToEx(hdcMem, xMouse, yMouse, NULL);

        xMouse = GET_X_LPARAM(lParam);
        yMouse = GET_Y_LPARAM(lParam);

        LineTo(hdc, xMouse, yMouse);
        LineTo(hdcMem, xMouse, yMouse);

        ReleaseDC(hwnd, hdc);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);//開始繪製窗口
        BitBlt(hdc, 0,0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);
        EndPaint(hwnd, &ps);
        return 0;
    case WM_CLOSE:
        if (IDOK == MessageBox(hwnd, TEXT("是否退出?"), TEXT("對話框"), MB_OKCANCEL | MB_DEFBUTTON1 | MB_ICONQUESTION))
        {
            DestroyWindow(hwnd);
        }
        else
        {
            return 0;
        }
    case WM_DESTROY:
        DeleteDC(hdcMem);
        DeleteObject(hBitmap);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
相關文章
相關標籤/搜索