• 第14章 位图和位块传输_14.4 GDI位图对象(2)


    14.4.7 在位图上绘图

    (1)在内存设备环境中绘图(与真实DC不同的是,内存DC的显示表面是个位图)

    (2)GetTextExtentPoint32函数:用于确定文本字符串的像素大小。(此大小就是与视频显示兼容的位图的尺寸)。

      参数

    说明

    hdc

    设备环境句柄

    lpString

    文本字符串,如szText

    cbString

    文本字符串中字符的个数。如lstrlen(szText)

    lpSize

    指向一个结构体,用来存放结果

    (3)当显示器的颜色深度和大小改变时,windows会自动改变内存设备环境的彩色分辩率。也就是内存设备环境与视频设备环境仍然会保持兼容。所以在WM_DISPLAYCHANGE消息中不用去关心这个问题。

    【HelloBit程序】

     效果图

      

    /*------------------------------------------------------------
    HELLOBIT.C -- Bitmap Demostration
    (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("HelloBit");
        HWND         hwnd;
        MSG          msg;
        WNDCLASSEX     wndclass;
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.cbSize = sizeof(WNDCLASSEX);
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(hInstance, szAppName);
        wndclass.hIconSm = LoadIcon(hInstance, szAppName);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndclass.lpszMenuName = szAppName;
        wndclass.lpszClassName = szAppName;
        if (!RegisterClassEx(&wndclass))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                       szAppName, MB_ICONERROR);
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,                  // window class name
                            TEXT("HelloBit"), // window caption
                            WS_OVERLAPPEDWINDOW,        // window style
                            CW_USEDEFAULT,              // initial x position
                            CW_USEDEFAULT,              // initial y position
                            CW_USEDEFAULT,              // initial x size
                            CW_USEDEFAULT,              // initial y size
                            NULL,                       // parent window handle
                            NULL,                       // window menu handle
                            hInstance,                  // program instance handle
                            NULL);                     // creation parameters
    
        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)
    {
        HDC            hdc;
        static  HDC    hdcMem;
        PAINTSTRUCT    ps;
        static int     cxBitmap, cyBitmap, cxClient, cyClient, iSize = IDM_BIG;
        static HBITMAP hBitmap;
        static TCHAR*  szText = TEXT("Hello,World!");
        SIZE size;
        HMENU          hMenu;
        switch (message)
        {
        case WM_CREATE:
            hdc = GetDC(hwnd);
            hdcMem = CreateCompatibleDC(hdc);
            GetTextExtentPoint32(hdc, szText, lstrlen(szText), &size);
            cxBitmap = size.cx;
            cyBitmap = size.cy;
            hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
            ReleaseDC(hwnd, hdc);
            SelectObject(hdcMem, hBitmap);
            TextOut(hdcMem, 0, 0, szText, lstrlen(szText));
            return 0;
        case WM_COMMAND:
            hMenu = GetMenu(hwnd);
            switch (LOWORD(wParam))  //菜单ID
            {
            case IDM_BIG:
            case IDM_SMALL:
                CheckMenuItem(hMenu, iSize, MF_UNCHECKED);
                iSize = LOWORD(wParam);
                CheckMenuItem(hMenu, iSize, MF_CHECKED);
                InvalidateRect(hwnd, NULL, TRUE);
                break;
            }
            return 0;
        case WM_SIZE:
            cxClient = LOWORD(lParam);
            cyClient = HIWORD(lParam);
            return 0;
        case WM_PAINT:
            hdc = BeginPaint(hwnd, &ps);
    
            switch (iSize)
            {
            case IDM_BIG:
                StretchBlt(hdc, 0, 0, cxClient, cyClient,
                           hdcMem, 0, 0, cxBitmap, cyBitmap, SRCCOPY);
                break;
            case IDM_SMALL:
                for (int y = 0; y < cyClient; y += cyBitmap)
                    for (int x = 0; x < cxClient; x += cxBitmap)
                    {
                        BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMem, 0, 0, SRCCOPY);
                    }
                break;
            }
    
            EndPaint(hwnd, &ps);
            return 0;
    
        case WM_DESTROY:
            DeleteDC(hdcMem);
            DeleteObject(hBitmap);
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    //resource.h

    //{{NO_DEPENDENCIES}}
    // Microsoft Developer Studio generated include file.
    // Used by HelloBit.rc
    //
    #define IDM_BIG                         40001
    #define IDM_SMALL                       40002
    
    // Next default values for new objects
    // 
    #ifdef APSTUDIO_INVOKED
    #ifndef APSTUDIO_READONLY_SYMBOLS
    #define _APS_NEXT_RESOURCE_VALUE        102
    #define _APS_NEXT_COMMAND_VALUE         40003
    #define _APS_NEXT_CONTROL_VALUE         1000
    #define _APS_NEXT_SYMED_VALUE           101
    #endif
    #endif

    //HelloBit.rc

    //Microsoft Developer Studio generated resource script.
    //
    #include "resource.h"
    
    #define APSTUDIO_READONLY_SYMBOLS
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 2 resource.
    //
    #include "afxres.h"
    
    /////////////////////////////////////////////////////////////////////////////
    #undef APSTUDIO_READONLY_SYMBOLS
    
    /////////////////////////////////////////////////////////////////////////////
    // English (U.S.) resources
    
    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
    #ifdef _WIN32
    LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
    #pragma code_page(1252)
    #endif //_WIN32
    
    #ifdef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // TEXTINCLUDE
    //
    
    1 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "#include ""afxres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Menu
    //
    
    HELLOBIT MENU DISCARDABLE 
    BEGIN
        POPUP "&Size"
        BEGIN
            MENUITEM "&Big",                        IDM_BIG, CHECKED
            MENUITEM "&Small",                      IDM_SMALL
        END
    END
    
    #endif    // English (U.S.) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED
    14.4.8 阴影位图

    (1)EnumDisplaySettings:该函数用来获取显示设备的参数信息,要想获取所有显示设备的所有信息,就要循环使用该函数。

    参数

    说明

    lpszDeviceName

    设备的名称,以NULL结尾的设备名称字符串。

    当NULL表示当前使用的显示设备

    iModeNum

    ①获取设备类型信息。该值可以是索引号或也可以使下值之一:

    ENUM_CURRENT_SETTINGS:获取当前显示设备的配置

    ENUM_REGISTRY_SETTINGS:注册表中所有当前存储的显示设备的配置。

    ②如果从0开始,要获得所有显示设备的物理模式,就要循环调用EnumDisplaySettings函数,直到返回返回值为FALSE。

    ③当以iModeNum为0而调用函数时,操作系统将初始化和填充相关显示设备信息。

    ④当以iModeNum为非0而调用函数时,操作系统将返回最后一次填充的信息。

    lpDevMode

    该参数是一个结构体用来接收物理模式的信息参数,在使用此结构体前要先初始化该结构体。函数会获取到以下五个字段的值:dmBitsPerpel、dmPelsWidth、dmPelsHeight(设备宽度和高度,以像素为单位,不会随分辨率而改变,可以用来测量显示器的大小!)、dmDisplayFlags、dmDisplayFrequency。

    (2)【Sketch程序】——先将图绘到内存设备,再绘到显示设备的DC环境。
    效果图

     

    /*------------------------------------------------------------
    SKETCH.C -- Shadow Bitmap 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("Sketch");
        HWND         hwnd;
        MSG          msg;
        WNDCLASSEX     wndclass;
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.cbSize = sizeof(WNDCLASSEX);
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(hInstance, szAppName);
        wndclass.hIconSm = LoadIcon(hInstance, szAppName);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndclass.lpszMenuName = NULL;
        wndclass.lpszClassName = szAppName;
        if (!RegisterClassEx(&wndclass))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                       szAppName, MB_ICONERROR);
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,                  // window class name
                            TEXT("Sketch"), // window caption
                            WS_OVERLAPPEDWINDOW,        // window style
                            CW_USEDEFAULT,              // initial x position
                            CW_USEDEFAULT,              // initial y position
                            CW_USEDEFAULT,              // initial x size
                            CW_USEDEFAULT,              // initial y size
                            NULL,                       // parent window handle
                            NULL,                       // window menu handle
                            hInstance,                  // program instance handle
                            NULL);                     // creation parameters
    
        //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图
        //如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况.
        if (hwnd == NULL)
        {
            MessageBox(NULL, TEXT("Not enough memory to create bitmap!"),
                       szAppName, MB_ICONERROR);
            return 0;
        }
        ShowWindow(hwnd, iCmdShow);
        UpdateWindow(hwnd);
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    //获得最大可显示的位图
    void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap)
    {
        DEVMODE devmode;
        int iModeNum = 0;
        *pcxBitmap = *pcyBitmap = 0;
        ZeroMemory(&devmode, sizeof(DEVMODE));
        devmode.dmSize = sizeof(DEVMODE);   //这个要记得设置!
        //EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。
        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:
            hdc = GetDC(hwnd);
            GetLargestDisplayMode(&cxBitmap, &cyBitmap);
            hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
            hdcMem = CreateCompatibleDC(hdc);
            ReleaseDC(hwnd, hdc);
            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 = LOWORD(lParam);
            yMouse = HIWORD(lParam);
            fLeftButtonDown = TRUE;
            return 0;
        case WM_LBUTTONUP:
            if (fLeftButtonDown)
                //SetCapture(NULL);
                ReleaseCapture();//课本用SetCapture(NULL);
    
            fLeftButtonDown = FALSE;
            return 0;
    
            //右键用来擦图(用白色绘图,等于在擦图)
        case WM_RBUTTONDOWN:
            if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
                SetCapture(hwnd);
            xMouse = LOWORD(lParam);
            yMouse = HIWORD(lParam);
            fRightButtonDown = TRUE;
            return 0;
        case WM_RBUTTONUP:
            if (fRightButtonDown)
                //SetCapture(NULL);
                ReleaseCapture();
            fRightButtonDown = FALSE;
            return 0;
        case WM_MOUSEMOVE:
            //己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键,
            //重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture
            //了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过,
            //所以仍会执行后面的代码
            if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出
                return 0;
            hdc = GetDC(hwnd);
            //左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔
            SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
            SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
            //同时在屏幕dc与内存dc中绘图
            MoveToEx(hdc, xMouse, yMouse, NULL);
            MoveToEx(hdcMem, xMouse, yMouse, NULL);
            xMouse = LOWORD(lParam);
            yMouse = HIWORD(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_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    14.4.9 在菜单中使用位图

    (1)GetMenuItemInfo(hMenu,uMenuItem,fByPosition,lpmii);

    ①uMenuItem:菜单ID或位置索引(看fByPositon是否是TRUE)

    ②lpmii,指一个MENUITEMINFO结构体

    MENUITEMINFO结构体

    字段

    说明

    cbSize

    结构的大小(字节)

    fMask

    MIIM_CHECKMARKS:获取或设置hbmpChecked和hbmpUnchecked成员

    MIIM_DATA:获取或设置dwItemData成员

    MIIM_ID :获取或设置wID成员

    MIIM_STATE: 获取或设置fState成员

    MIIM_SUBMENU: 获取或设置hSubMenu成员

    MIIM_TYPE :获取或设置fType和dwTypeData成员

    fType

    菜单项类型

    MFT_BITMAP:使用一个位图显示菜单项.dwTypeData低位字是该位图的句柄.并且cch被忽视

    MFT_MENUBARBREAK:放置菜单项在新行上(适用于菜单栏)或在新列内

    MFT_SEPARATOR:指定那个菜单项是一个分隔条

    MFT_STRING:用一个文本字符串显示菜单项.dwTypeData成员指示一个以NULL结尾的字符串

    MFT_RADIOCHECK:如果hbmpChecked成员是NULL,显示选中的菜单项使用一个单选按钮来代替一个复选标记

    fState

    菜单项的状态

    MFS_CHECKED:复选的菜单项.至于更多关于菜单项选中的信息,看hbmpChecked成员

    MFS_DISABLED:菜单项无效并变灰使得它不能被选择.等效于MFS_GRAYED

    MFS_ENABLED: 激活菜单项使它可以被选择

    MFS_GRAYED:菜单项无效并变灰使得它不能被选择.等效于MFS_DISABLED

    MFS_HILITE:菜单项高亮显示

    MFS_UNCHECKED:取消复选菜单项

    MFS_UNHILITE:移除菜单项的高亮显示,这是默认状态

    wID

    菜单项ID, 只有在设置了fMask的MIIM_ID时才能使用

    hSubMenu

    菜单项相关联的下拉菜单或子菜单的的句柄。如果菜单项不是一个打开的下拉菜单或子菜单,那这个成员是NULL, 该项只有在设置了fMask的MIIM_SUBMENU时才能使用

    hbmpChecked

    菜单项被选中时显示在一侧的位图的句柄。

    hbmpUnchecked

    菜单项没有被选中时显示在一侧的位图的句柄

    dwItemData

    应用程序定义的菜单项相关联的值,该项只有在设置了fMask的MIIM_DATA时才能使用

    dwTypeData

    菜单项的内容,它的具体意义依赖于fTYPE值,并且它只能在fMask设置了MIIM_TYPE标记时才能被使用;

    要获取一个MFT_STRING类型的菜单项,首先要得到该字符串的大小,通过设置MENUITEMINFO结构的dwTypeData值为空并调用函数GetMenuItemInfo得到的cch值就是字符串的大小,然后分配一个字符串大小的缓冲区,把指向缓冲区的指针存赋给dwTypeData并再次调用GetMenuItemInfo函数用字符串来填充缓冲区。

    如果获取其它类型的菜单项,GetMenuItemInfo函数会赋给dwTypeData一个类型由fType成员指定的值。当使用SetMenuItemInfo函数时,dwTypeData必须包含一个类型由fType成员指定的值,该项只有在设置了fMask成员的MIIM_STRING标记时才能使用。

    cch

    当检索一个MFT_STRING类型菜单项的信息时,为菜单项文本(TCHAR)的长度

    hbmpItem

    菜单项上显示位图的句柄,它可能是以下标记中的一个,该项只有在设置了fMask成员的MIIM_BITMAP标记时才能使用。

    HBMMENU_CALLBACK:一个由拥有该菜单的窗口绘制的位图

    HBMMENU_MBAR_CLOSE:菜单栏的关闭按钮

    HBMMENU_MBAR_MINIMIZE:菜单栏的最小化按钮

    HBMMENU_MBAR_RESTORE:菜单栏的还原按钮

    ……

    (2)使用位图做菜单项时的注意事项

    ①Windows会调整菜单栏高度来适应最高的位图,其他位图(包括文字)和菜单上沿顶端对齐。

    ②如果把一个位图放入顶级菜单,那么SM_CYMENU调用和GetSystemMetrics得到的菜单高度值不同。

    ③当菜单包含文本时,Windows会自动添加键盘接口。但一旦加入位图,就没有键盘接口了。可以使用WM_MENUCHAR消息,在按下Alt键以及没有映射到任何菜单项的某个字母键时,Windows会向应用程序发送一个WM_MENUCHAR消息。wParam为被按下键的ASCII码。如果和某个菜单项对应,就要向Windows返回一个双字(即return的返回值),并将高位字设为2(即MNC_EXECUTE),将低位字设成对应菜单项的索引。Windows会向窗口发送WM_COMMAND消息。

     【GrafMenu程序】
    效果图

    /*------------------------------------------------------------
    SKETCH.C -- Shadow Bitmap 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("Sketch");
        HWND         hwnd;
        MSG          msg;
        WNDCLASSEX     wndclass;
        wndclass.style = CS_HREDRAW | CS_VREDRAW;
        wndclass.cbSize = sizeof(WNDCLASSEX);
        wndclass.lpfnWndProc = WndProc;
        wndclass.cbClsExtra = 0;
        wndclass.cbWndExtra = 0;
        wndclass.hInstance = hInstance;
        wndclass.hIcon = LoadIcon(hInstance, szAppName);
        wndclass.hIconSm = LoadIcon(hInstance, szAppName);
        wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
        wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
        wndclass.lpszMenuName = NULL;
        wndclass.lpszClassName = szAppName;
        if (!RegisterClassEx(&wndclass))
        {
            MessageBox(NULL, TEXT("This program requires Windows NT!"),
                       szAppName, MB_ICONERROR);
            return 0;
        }
    
        hwnd = CreateWindow(szAppName,                  // window class name
                            TEXT("Sketch"), // window caption
                            WS_OVERLAPPEDWINDOW,        // window style
                            CW_USEDEFAULT,              // initial x position
                            CW_USEDEFAULT,              // initial y position
                            CW_USEDEFAULT,              // initial x size
                            CW_USEDEFAULT,              // initial y size
                            NULL,                       // parent window handle
                            NULL,                       // window menu handle
                            hInstance,                  // program instance handle
                            NULL);                     // creation parameters
    
        //因为在CreateWindow会发送WM_CREATE,在WM_CREATE消息中会创建一个位图
        //如果位图太大,会导致创建失败,WM_CREATE会返回-1,如下处理这种情况.
        if (hwnd == NULL)
        {
            MessageBox(NULL, TEXT("Not enough memory to create bitmap!"),
                       szAppName, MB_ICONERROR);
            return 0;
        }
        ShowWindow(hwnd, iCmdShow);
        UpdateWindow(hwnd);
    
        while (GetMessage(&msg, NULL, 0, 0))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return msg.wParam;
    }
    //获得最大可显示的位图
    void GetLargestDisplayMode(int* pcxBitmap, int* pcyBitmap)
    {
        DEVMODE devmode;
        int iModeNum = 0;
        *pcxBitmap = *pcyBitmap = 0;
        ZeroMemory(&devmode, sizeof(DEVMODE));
        devmode.dmSize = sizeof(DEVMODE);   //这个要记得设置!
        //EnumDisplaySettins第1个参数为NULL,表示当前正在使用的显示设备,可以不止一个。
        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:
            hdc = GetDC(hwnd);
            GetLargestDisplayMode(&cxBitmap, &cyBitmap);
            hBitmap = CreateCompatibleBitmap(hdc, cxBitmap, cyBitmap);
            hdcMem = CreateCompatibleDC(hdc);
            ReleaseDC(hwnd, hdc);
            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 = LOWORD(lParam);
            yMouse = HIWORD(lParam);
            fLeftButtonDown = TRUE;
            return 0;
        case WM_LBUTTONUP:
            if (fLeftButtonDown)
                //SetCapture(NULL);
                ReleaseCapture();//课本用SetCapture(NULL);
    
            fLeftButtonDown = FALSE;
            return 0;
    
            //右键用来擦图(用白色绘图,等于在擦图)
        case WM_RBUTTONDOWN:
            if (!fLeftButtonDown) //因为当左键按下时,说明正在用白色擦图,此时鼠标是被捕捉的
                SetCapture(hwnd);
            xMouse = LOWORD(lParam);
            yMouse = HIWORD(lParam);
            fRightButtonDown = TRUE;
            return 0;
        case WM_RBUTTONUP:
            if (fRightButtonDown)
                //SetCapture(NULL);
                ReleaseCapture();
            fRightButtonDown = FALSE;
            return 0;
        case WM_MOUSEMOVE:
            //己知问题:当鼠标在客户区内按下一个键时并拖动绘图,到客户区外依次释放两个键,
            //重回客户区时,这里鼠标己经松开,但仍会画图——原因是当客户区外释放时,就ReleaseCapture
            //了,导致其中一个鼠标按键的ButtonUp消息收不到,即仍处于fButtonDown状态,从而下列的判断会通过,
            //所以仍会执行后面的代码
            if (!fLeftButtonDown && !fRightButtonDown) //左右键都没按下时,退出
                return 0;
            hdc = GetDC(hwnd);
            //左键按下时,选黑色画笔,右键时白色画笔。同时按下时仍然是黑色的画笔
            SelectObject(hdc, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
            SelectObject(hdcMem, GetStockObject(fLeftButtonDown ? BLACK_PEN : WHITE_PEN));
            //同时在屏幕dc与内存dc中绘图
            MoveToEx(hdc, xMouse, yMouse, NULL);
            MoveToEx(hdcMem, xMouse, yMouse, NULL);
            xMouse = LOWORD(lParam);
            yMouse = HIWORD(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_DESTROY:
            PostQuitMessage(0);
            return 0;
        }
        return DefWindowProc(hwnd, message, wParam, lParam);
    }

    //resource.h

    //{{NO_DEPENDENCIES}}
    // Microsoft Developer Studio generated include file.
    // Used by GrafMenu.rc
    //
    #define IDM_FONT_COUR                   101
    #define IDM_FONT_ARIAL                  102
    #define IDM_FONT_TIMES                  103
    #define IDM_HELP                        104
    #define IDM_EDIT_UNDO                   40005
    #define IDM_EDIT_CUT                    40006
    #define IDM_EDIT_COPY                   40007
    #define IDM_EDIT_PASTE                  40008
    #define IDM_EDIT_CLEAR                  40009
    #define IDM_FILE_NEW                    40010
    #define IDM_FILE_OPEN                   40011
    #define IDM_FILE_SAVE                   40012
    #define IDM_FILE_SAVE_AS                40013
    
    // Next default values for new objects
    // 
    #ifdef APSTUDIO_INVOKED
    #ifndef APSTUDIO_READONLY_SYMBOLS
    #define _APS_NEXT_RESOURCE_VALUE        108
    #define _APS_NEXT_COMMAND_VALUE         40014
    #define _APS_NEXT_CONTROL_VALUE         1000
    #define _APS_NEXT_SYMED_VALUE           105
    #endif
    #endif

    //GrafMenu.rc

    //Microsoft Developer Studio generated resource script.
    //
    #include "resource.h"
    
    #define APSTUDIO_READONLY_SYMBOLS
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 2 resource.
    //
    #include "afxres.h"
    
    /////////////////////////////////////////////////////////////////////////////
    #undef APSTUDIO_READONLY_SYMBOLS
    
    /////////////////////////////////////////////////////////////////////////////
    // English (U.S.) resources
    
    #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
    #ifdef _WIN32
    LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
    #pragma code_page(1252)
    #endif //_WIN32
    
    #ifdef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // TEXTINCLUDE
    //
    
    1 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "resource.h"
    END
    
    2 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "#include ""afxres.h""
    "
        ""
    END
    
    3 TEXTINCLUDE DISCARDABLE 
    BEGIN
        "
    "
        ""
    END
    
    #endif    // APSTUDIO_INVOKED
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Menu
    //
    
    MENUFILE MENU DISCARDABLE 
    BEGIN
        MENUITEM "&New",                        IDM_FILE_NEW
        MENUITEM "&Open...",                    IDM_FILE_OPEN
        MENUITEM "&Save",                       IDM_FILE_SAVE
        MENUITEM "Save &As...",                 IDM_FILE_SAVE_AS
    END
    
    MENUEDIT MENU DISCARDABLE 
    BEGIN
        MENUITEM "&Undo",                       IDM_EDIT_UNDO
        MENUITEM SEPARATOR
        MENUITEM "Cu&t",                        IDM_EDIT_CUT
        MENUITEM "&Copy",                       IDM_EDIT_COPY
        MENUITEM "&Paste",                      IDM_EDIT_PASTE
        MENUITEM "De&lete",                     IDM_EDIT_CLEAR
    END
    
    
    /////////////////////////////////////////////////////////////////////////////
    //
    // Bitmap
    //
    
    BITMAPFONT              BITMAP  DISCARDABLE     "Fontlabl.bmp"
    BITMAPHELP              BITMAP  DISCARDABLE     "Bighelp.bmp"
    BITMAPEDIT              BITMAP  DISCARDABLE     "Editlabl.bmp"
    BITMAPFILE              BITMAP  DISCARDABLE     "Filelabl.bmp"
    #endif    // English (U.S.) resources
    /////////////////////////////////////////////////////////////////////////////
    
    
    
    #ifndef APSTUDIO_INVOKED
    /////////////////////////////////////////////////////////////////////////////
    //
    // Generated from the TEXTINCLUDE 3 resource.
    //
    
    
    /////////////////////////////////////////////////////////////////////////////
    #endif    // not APSTUDIO_INVOKED
  • 相关阅读:
    轮子来袭 vJine.Core Orm 之 03_架构分析
    轮子来袭 vJine.Core 之 AppConfig<T>
    C# 数据类型映射 (SQLite,MySQL,MSSQL,Oracle)
    炫酷 2048 完全免费 色彩无线 流畅如飞
    DebugLog 打印方法执行时间
    [转] charles使用教程指南
    Macbook Pro配置PHP开发环境
    基于ubuntu 14搭建nginx+php+mysql环境
    Android Studio -修改LogCat的颜色
    [转]
  • 原文地址:https://www.cnblogs.com/5iedu/p/4698468.html
Copyright © 2020-2023  润新知