Summary of Windows Programming Core (GDI Bitmap Object - 2018.5.8)

DDB: Device-related bitmap, which belongs to GDI object, and is stored inside the GDI module like brushes and brushes.
HBITMAP hBitmap;//Store a handle to DDB

1. Create DDB

(1) hBitmap=CreateBitmap(cx,cy,cPlanes,cBitsPixel,bits)
/
This function is the first to create a GDI bitmap object, also called a device-dependent bitmap (DDB).
There are two cases for the setting of the third and fourth parameters of this function:
1. cPlanes, cBitsPixel are both 1, which means that the bitmap is a monochrome bitmap.
2. The value of cPlanes, cBitsPixel is equal to the value in a certain device environment, and the GetDeviceCaps function is called through the PLANES and BITSPIXEL indexes to obtain the values ​​of these two parameters.
/

(2) hBitmap=CreateCompatibleBitmap(hdc,cx,cy)
/*
This function creates a GDI bitmap object compatible with the device.
What is device compatibility? In fact, the bitmap has the same color organization (cPlanes, cBitsPixel) as the real device (video display). Why Compatible with Devices? Think about it, if the color organization of the bitmap I created is not the same as the color organization of the device, then it is possible that the color that the bitmap wants to display may not be displayed in the device, and then something goes wrong.

This function uses the device environment handle specified by parameter 1 to obtain the information returned by GetDeviceCaps, and then passes the information to CreateBitmap, and then returns the GDI bitmap object handle.
*/

(3) hBitmap=CreateBitmapIndirect(&bitmap)
/*
The parameter bitmap is a structure of type BITMAP, which is defined as follows:

typedef struct tagBITMAP {
  LONG   bmType;//位图类型,该字段必须为0
  LONG   bmWidth;//横向像素个数
  LONG   bmHeight;//纵向像素个数
  LONG   bmWidthBytes;//不需要设置,Windows会自动计算这个字段的值
  WORD   bmPlanes;//颜色平面数
  WORD   bmBitsPixel;//每个像素的颜色位数
  LPVOID bmBits;//设为NULL或某个带有值的字节类型数组的地址来初始化位图的所有像素位
} BITMAP, *PBITMAP;

You can use the GetObject function to obtain the information of the specified bitmap object. For example:
BITMAP bitmap;
GetObject(hBitmap,sizeof(BITMAP),&bitmap);
//Parameter 1: Specify the GDI bitmap object to be obtained
*/

Summary: These functions can create GDI bitmap objects, and then Windows allocates and initializes these bitmap objects in memory, and stores bitmap object information, such as: the number of horizontal and vertical pixels, the number of color planes, the number of pixels per pixel Color bits and color values ​​per pixel. Note that after creating a GDI bitmap object and using the object, you must call the DeleteObject function to delete all bitmaps created in the program.

2. Pixel bit problem of bitmap

We all know that a bitmap consists of many rectangular pixels, and the color bits of each pixel are either 1 bit (monochrome bitmap) or multiple bits (color bitmap), so each pixel has a color value. Windows provides two functions to set or get all pixel bits of a bitmap after it is created.
1.SetBitmapBits(hBitmap,cBytes,&bits);
2.GetBitmapBits(hBitmap,cBytes,&bits);

Closer to home, assuming that a certain pixel of a certain DDB (8 color bits per pixel) has a color value of 0x37, what color does this represent. In fact, DDB does not have a color table, but when the DDB is displayed on the screen, the displayed pixel corresponds to the RGB color with index 0x37 in the color lookup table of the video card, so this is called device-dependent. The color of the pixels in the bitmap is device-dependent, so this is called device-dependent. From the analysis here, we can think that if we don't know the color corresponding to the color lookup table of the video card, can we customize all the pixel bits of the bitmap when we create the DDB? No, this is not possible. Therefore, Windows has such a rule: For color DDB, you should not call CreateBitmap or CreateBitmapIndirect or SetBitmapBits function to set all pixel bits of the bitmap, only for monochrome DDB, you can safely set the pixel bits, because only 0 for monochrome Or 1, everyone knows.

3. Memory device environment

After we create a GDI bitmap object (DDB), the memory device environment is required to use the GDI bitmap object, because only the memory device environment can select the bitmap, and other types of device environments cannot. The memory device context, on the other hand, can select other GDI objects (pens, brushes, fonts, palettes, and regions). Generally, the device environment corresponds to a specific graphics output device (video display or printer), while the memory device environment only exists in memory. It is not a real graphics output device, but it is "compatible" with these real devices, meaning It means that the memory device context and the real device context have the same color organization. In order to realize the "compatibility" between the memory device environment and the real device, when creating the memory device environment, there must be a device handle corresponding to the real device, for example:
hdcMem=CreateCompatibleDC(hdc);//If the parameter is set to NULL, Windows will then create a memory device context compatible with the video display.

A real device, such as a video display, is a dot-matrix device, and a memory device environment also has a display surface, however this display surface is only 1 pixel wide, 1 pixel high, and monochrome. Therefore, if you want to do things in the memory device environment, you must increase the display surface. To do this, you can select the GDI bitmap object you just created into the memory device environment. For example:
SelectObject(hdcMem,hBitmap);

Note: Whether the SelectObject function call is valid or not depends on two situations:
(1) The bitmap is a monochrome bitmap.
(2) The bitmap has the same color organization as the real device that is compatible with the memory device context. This means that when creating a GDI bitmap object, the SelectObject function will only work if the bitmap is compatible with the real device.
Summarized in one sentence: If the bitmap is a single ×××, it is unobstructed and can be directly selected into the memory device environment. Creating a color bitmap needs to be compatible with real devices, and creating a memory device environment needs to be compatible with real devices, then calling the SelectObject function to select the bitmap into the memory device environment will succeed.

We can do anything in the memory device environment like in the real device environment, such as: drawing and so on. Note that the drawing operation is to draw on the bitmap, and the original bitmap is overwritten by the new bitmap.

4. Load bitmap resources

In addition to using the bitmap creation function to create bitmap objects, you can also load bitmap resources. This method ensures that the loaded bitmap objects and video displays have the same color organization, that is, they are compatible with video displays.
For example:
hBitmap=LoadBitmap(hInstance,szBitmapName);
/
The usage of this function is the same as loading the icon and the mouse pointer, but it is worth noting that an error will be reported when specifying the system preset identifier when using the system bitmap, which needs to be set in windows.h Add #define OEMRESOURCE before the header file.
/

All bitmap objects loaded by the LoadBitmap function should finally be deleted with the DeleteObject function.

Code example:

#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. Monochrome bitmap format

As can be seen from the above, Windows provides a rule: For color DDB, you should not call the CreateBitmap or CreateBitmapIndirect or SetBitmapBits functions to set all pixel bits of the bitmap. Only for monochrome DDB, you can safely set the pixel bits, because monochrome only 0 or 1, everyone knows. So, if the bitmap is just a monochrome bitmap, then I can manually create a bitmap object myself and initialize all the pixel bits of the bitmap with a byte array, since the pixel bits are either 1 bit, 1 (white) or 0 (black) only.

Code example:
/
The following code is to repeatedly display the bitmap of the memory device environment in the client area, forming a real brick wall!
/

#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. Bitmap brushes

Uh, this is actually nothing to say, just add the bitmap object to the brush, and then use the brush to draw the background of the client area.

Code example:

#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. Drawing on Bitmap

First create a bitmap object, then select the bitmap object into the memory device environment, and then draw in the memory device environment, then the original bitmap is overwritten by the drawn bitmap. Then call the BitBlt or StretchBlt function to copy the bitmap of the memory device context into the client area device context.

#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. Shadow Bitmap

Press the left mouse button and drag the mouse to paint with a black brush, and press the right mouse button and drag the mouse to paint with a white brush. It is worth noting that we call SetCapture(hwnd) in the code example, indicating that when the mouse is outside the client area, the mouse message can also be received. We can do a small test, first comment out the BitBlt( hdc, 0,0, cxClient, cyClient, hdcMem, 0, 0, SRCCOPY);, found no, if you press the left mouse button and drag the mouse, when you drag it outside the client area, it is actually saved in the memory device environment The graph drawn outside the client area, because the size of the memory device environment is based on the maximum size of the client area, and then we change the window size, and the client area has nothing. This is because there is no continued copying and saving from the memory device environment. Bitmap to the client area device context.

Code example:

#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);
}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325947402&siteId=291194637