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

 14.4.10 非矩形的位图图像

(1)“掩码”位图——单色位图,要显示的像素对应的掩码置1,不显示置0
(2)光栅操作(点这里,见此文分析) 
(3)MaskBlt函数

 

①MaskBlt(hdcDest, nXDest, nYDest, nWidth, nHeight, hdcSrc, nXSrc, nYSrc,hbmMask, xMask, yMask, dwRop);

②前景和背景:在由hbmMask指定的掩码中,数值1表示在那个位置应使用dwRop指定的前景光栅操作码。数值0表示应使用dwRop指定的背景光栅操作码。

③用MAKEROP4(fore,back)组合光栅操作码
【BitMask程序】
效果图

/*------------------------------------------------------------
BITMASK.C -- Bitmap Masking 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("BitMask");
    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(LTGRAY_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("Bitmap Masking Demo"), // 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)
{
    PAINTSTRUCT ps;
    static int cxBitmap, cyBitmap, cxClient, cyClient;
    static HINSTANCE   hInstance;
    static HBITMAP hBitmapImag, hBitmapMask;
    HDC          hdc, hdcMemImag, hdcMemMask;
    BITMAP bitmap;
    int x, y;
    switch (message)
    {
    case WM_CREATE:
        hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
        //加载原始图像并获取图像大小
        hBitmapImag = LoadBitmap(hInstance, TEXT("Matthew"));
        GetObject(hBitmapImag, sizeof(BITMAP), &bitmap);
        cxBitmap = bitmap.bmWidth;
        cyBitmap = bitmap.bmHeight;
        //将原始图像选入内存DC
        hdcMemImag = CreateCompatibleDC(NULL); //这里用NULL没关系,只要是借个DC来修改原始图像
        SelectObject(hdcMemImag, hBitmapImag);
        //创建单色掩码位图和内存DC
        hBitmapMask = CreateBitmap(cxBitmap, cyBitmap, 1, 1, NULL); //单色cxBitmap*cybitmap像素位图
        hdcMemMask = CreateCompatibleDC(NULL); //这里用NULL没关系,只要是借个DC来修改掩码位图
        SelectObject(hdcMemMask, hBitmapMask);
        //给掩码位图上色:矩形黑色,椭圆白色。
        SelectObject(hdcMemMask, GetStockObject(BLACK_BRUSH));
        Rectangle(hdcMemMask, 0, 0, cxBitmap, cyBitmap);
        SelectObject(hdcMemMask, GetStockObject(WHITE_BRUSH));
        Ellipse(hdcMemMask, 0, 0, cxBitmap, cyBitmap);
        //遮罩原始图像,只显示椭圆内的图像
        BitBlt(hdcMemImag, 0, 0, cxBitmap, cyBitmap,
               hdcMemMask, 0, 0, SRCAND); //这里用“与”操作,只显示掩码为1的,但周围是黑色的。

        DeleteDC(hdcMemImag); //可以删除,因为图像操作结果己保存在hBitmapImag中了。
        DeleteDC(hdcMemMask); //可以删除,因为图像操作结果己保存在hBitmapMask中了。
        return 0;
    case WM_SIZE:
        cxClient = LOWORD(lParam);
        cyClient = HIWORD(lParam);
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);

        //
        hdcMemImag = CreateCompatibleDC(hdc); //这里要显示在客户区了,不用要NULL
        SelectObject(hdcMemImag, hBitmapImag);
        hdcMemMask = CreateCompatibleDC(hdc);
        SelectObject(hdcMemMask, hBitmapMask);

        x = (cxClient - cxBitmap) / 2;
        y = (cyClient - cyBitmap) / 2;
        ////掩码位图,至此是一个外部黑色的,里面为白色的椭圆
        //0x220326=D&(~S),让椭圆内部为黑,外部白色,再&D,此时屏幕上为一个黑色椭圆。
        //BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMemMask, 0, 0, 0x220326); 

        //源和目标进行逻辑或运算,或的结果就是黑色,区域将源和目标重叠部分(椭圆区域)显示出来。                                                             
        //BitBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, SRCPAINT);
        //以上两句也可以用MaskBlt代替:MAKEROP4(fore,back),掩码为1为前景,掩码为0的地方为背景。
        //因掩码图椭圆外为黑色,内部白色。因此前景的地方直接拷贝照片相应的地方(SRCCOPY),
        //背景的地方用画刷去刷(SRCPAINT)
        MaskBlt(hdc, x, y, cxBitmap, cyBitmap, hdcMemImag, 0, 0, hBitmapMask, 0, 0, MAKEROP4(SRCCOPY, SRCPAINT));
        DeleteDC(hdcMemImag);
        DeleteDC(hdcMemMask);
        EndPaint(hwnd, &ps);
        return 0;

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

 //resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 BitMask.rc 使用
//
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

 //BitMask.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""
"
""
END
3 TEXTINCLUDE
BEGIN
"
"
""
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
MATTHEW             BITMAP                  "Matthew.bmp"
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
14.4.11 简单的动画效果

(1)利用BitBlt在内存设备环境中画出小球

(2)通过计时器实现动画

【Bounce程序】
效果图

/*------------------------------------------------------------
BOUNCE.C -- Bouncing Ball Program
(c) Charles Petzold, 1998
------------------------------------------------------------*/
#include <windows.h>
#define ID_TIMER 1
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("Bounce");
    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("Bouncing Ball"), // 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)
{
    static HBITMAP hBitmap;
    HDC         hdc, hdcMem;
    HBRUSH      hBrush;
    static int xPixel, yPixel, cxRadius, cyRadius, xCenter, yCenter, cxClient, cyClient,
        cxTotal, cyTotal, cxMove, cyMove;
    int iScale;
    switch (message)
    {
    case WM_CREATE:
        hdc = GetDC(hwnd);
        xPixel = GetDeviceCaps(hdc, ASPECTX);
        yPixel = GetDeviceCaps(hdc, ASPECTY);
        ReleaseDC(hwnd, hdc);
        SetTimer(hwnd, ID_TIMER, 50, NULL);
        return 0;
    case WM_SIZE:
        xCenter = (cxClient = LOWORD(lParam)) / 2;
        yCenter = (cyClient = HIWORD(lParam)) / 2;
        //小球的半径是客户区高、宽中较小一个的16分之一
        iScale = min(cxClient*xPixel, cyClient*yPixel) / 16;
        cxRadius = iScale / xPixel;
        cyRadius = iScale / yPixel;
        //cxMove = 半径的1/2
        cxMove = max(1, cxRadius / 2);
        cyMove = max(1, cyRadius / 2);
        //创建一个比球的尺寸多出半径一半的位图,目的是当图运动步长较小时,仍可以用后一张图
        //覆盖住前一张,己实现擦图。
        cxTotal = 2 * (cxRadius + cxMove);
        cyTotal = 2 * (cyRadius + cyMove);
        if (hBitmap)
            DeleteObject(hBitmap);
        hdc = GetDC(hwnd);
        hdcMem = CreateCompatibleDC(hdc);
        hBitmap = CreateCompatibleBitmap(hdc, cxTotal, cyTotal);  //注意是hdc,而不是hdcMem
        ReleaseDC(hwnd, hdc);
        SelectObject(hdcMem, hBitmap);
        //将整个位图填上白色(含边框)
        Rectangle(hdcMem, -1, -1, cxTotal + 1, cyTotal + 1);
        //创建球,内部用对角线填充
        hBrush = CreateHatchBrush(HS_DIAGCROSS, RGB(255, 0, 0));
        SelectObject(hdcMem, hBrush);
        SetBkColor(hdcMem, RGB(255, 0, 255));
        Ellipse(hdcMem, cxMove, cxMove, cxTotal - cxMove, cyTotal - cyMove);
        DeleteDC(hdcMem);
        DeleteObject(hBrush);
        return 0;
    case WM_TIMER:
        if (!hBitmap) break;

        hdc = GetDC(hwnd);
        hdcMem = CreateCompatibleDC(hdc);
        SelectObject(hdcMem, hBitmap);
        BitBlt(hdc, xCenter - cxTotal / 2, yCenter - cyTotal / 2, cxTotal, cyTotal,
               hdcMem, 0, 0, SRCCOPY);
        DeleteDC(hdcMem);
        ReleaseDC(hwnd, hdc);
        xCenter += cxMove;  //因为步长小,后一幅图大小仍会将前一张图球的那部分覆盖掉
        yCenter += cyMove;  //从而实现了擦图,如果步长大的话就会留下痕迹。
        if ((xCenter + cxRadius >= cxClient) || (xCenter - cxRadius <= 0))
            cxMove = -cxMove;
        if ((yCenter + cyRadius >= cyClient) || (yCenter - cyRadius <= 0))
            cyMove = -cyMove;
        return 0;

    case WM_DESTROY:
        if (hBitmap) DeleteObject(hBitmap);
        KillTimer(hwnd, ID_TIMER);
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}
14.4.12 窗口以外的位图

(1)GetDCEx:GetDCEx(HWND hWnd, HRGN hrgnClip, DWORD flags)

flags的值

DCX_WINDOW

返回与窗口矩形而不是与客户矩形相对应

DXC_CACHE

从高速缓存而不是从OWNDC或CLASSDC窗口中返回设备环境。从本质上覆盖CS_OWNDC和CS_CLASSDC

DCX_PARENTCLIP

使用父窗口的可见区域,父窗口的WS_CIPCHILDREN和CS_PARENTDC风格被忽略,并把设备DC环境的原点,设在由hWnd所标识的窗口的左上角

DCX_CLIPSIBLINGS

排除hWnd参数所标识窗口上的所有兄弟窗口的可见区域

DCX_CLIPCHILDREN

排除hWnd参数所标识窗口上的所有子窗口的可见区域

DCX_NORESETATTRS

当设备DC环境被释放时,并不重置该DC的特性为缺省特性

DCX_LOCKWINDOWUPDATE

即使在指定窗口被LockWindowUpdate的情况下也会绘制,该参数用于在跟踪期间进行绘制

DCX_EXCLUDERGN

从返回设备DC环境的可见区域中排除由hrgnClip指定的剪切区域

DCX_INTERSECTRGN

对hrgnClip指定的剪切区域与返回设备描述的可见区域作交运算

DCX_VALIDATE

当与DCX_INTERSECTUPDATE一起指定时,致使设备DC文环境完全有效,该函数与DCX_INTERSECTUPDATE和DCX_VALIDATE一起使用时与使用BeginPaint函数相同。

【Scramble程序】——打乱桌面并自动恢复(利用锁定屏幕更新技术)

 效果图

/*----------------------------------------------------------------
SCRAMBLE.C —— Scramble (and UnScramble) Screen
-----------------------------------------------------------------*/
#include <windows.h>
#define NUM 200
//注意,这个程序没有窗口过程,只能WinMain主函数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PTSTR szCmdLine, int iCmdShow)
{
    static  int iKeep[NUM][4];
    HDC  hdcSrc, hdcMem;
    HBITMAP hBitmap;
    HWND hwnd;
    int cx, cy;
    int x1, y1, x2, y2;
    hwnd = GetDesktopWindow();
    if (LockWindowUpdate(hwnd))
    {
        //GetDCEx的参数DCX_LOCKWINDOWUPDATE确保本程序在屏幕锁定期间仍可更新屏幕
        hdcSrc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE);
        hdcMem = CreateCompatibleDC(hdcSrc);
        cx = GetSystemMetrics(SM_CXSCREEN) / 10;  //位图大小为屏幕的1/10
        cy = GetSystemMetrics(SM_CYSCREEN) / 10;
        hBitmap = CreateCompatibleBitmap(hdcSrc, cx, cy);
        SelectObject(hdcMem, hBitmap);

        srand((int)GetCurrentTime());
        for (int i = 0; i < 2; i++)
            for (int j = 0; j < NUM; j++)
            {
                if (i == 0) //打乱
                {
                    //rand()产生0-RAND_MAX之间的数,其中RAND_MAX为0x7fff
                    //随机产生两个矩形并保存起来
                    iKeep[j][0] = x1 = cx*(rand() % 10);
                    iKeep[j][1] = y1 = cy*(rand() % 10);
                    iKeep[j][2] = x2 = cx*(rand() % 10);
                    iKeep[j][3] = y2 = cy*(rand() % 10);
                } else      //恢复时,要后面开始
                {
                    x1 = iKeep[NUM - 1 - j][0];
                    y1 = iKeep[NUM - 1 - j][1];
                    x2 = iKeep[NUM - 1 - j][2];
                    y2 = iKeep[NUM - 1 - j][3];
                }

                //交换两个矩形内的图像
                BitBlt(hdcMem, 0, 0, cx, cy, hdcSrc, x1, y1, SRCCOPY);  //第1张位图保存
                BitBlt(hdcSrc, x1, y1, cx, cy, hdcSrc, x2, y2, SRCCOPY);//第2张位图复制到第1张的位置
                BitBlt(hdcSrc, x2, y2, cx, cy, hdcMem, 0, 0, SRCCOPY);  //从内存DC中复制第1张到第2张的位置
                Sleep(2);
            }
        DeleteDC(hdcMem);
        ReleaseDC(hwnd, hdcSrc);
        DeleteObject(hBitmap);
        LockWindowUpdate(NULL); //屏幕解锁
    }

    return 0;
}

(2)屏幕截图

   ①剪贴板中使用位图时,可以不是全局句柄,而直接使用位图句柄

   ②SetStretchBltMode(hdc, COLORONCOLOR);//缩小图片时,删除所有消除的像素行,不保留其信息

【BlowUp程序】——操作方法

  ①在客户区按下鼠标左键,鼠标变成十字形。

  ②在按键左键的同时,将指针移到屏幕任意地方,放在要截图位置的左上角。

  ③保持按住左键的同时,按下右键,并拖出一个矩形框(会反色显示)的右下角,再松开鼠标(松开顺序无关紧要)

  ④如果从右上角向左下角选取,可以得到左右相反图像。从左下向右上角选择会得到上下相反的图像。从右下向左上,会出现上下、左右都相反的图像。

效果图

/*------------------------------------------------------------
BLOWUP.C -- Video Magnifier Program (放大镜)
(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("BlowUp");
    HWND         hwnd;
    MSG          msg;
    WNDCLASSEX     wndclass;
    HACCEL       hAccel;
    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("Blow-UP Mouse Demo"), // 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);
    hAccel = LoadAccelerators(hInstance, szAppName);
    while (GetMessage(&msg, NULL, 0, 0))
    {
        if (!TranslateAccelerator(hwnd, hAccel, &msg))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    return msg.wParam;
}
//将目标矩形内颜色取反,以表示被选中的区域
void InvertBlock(HWND hwndSrc, HWND hwnd, POINT ptBeg, POINT ptEnd)
{
    HDC hdc;
    hdc = GetDCEx(hwndSrc, NULL, DCX_CACHE | DCX_LOCKWINDOWUPDATE); //获屏幕DC并允许锁屏期间也可绘图
    ClientToScreen(hwnd, &ptBeg);  //因为屏幕捕获的原因,这里传进来的是客户区坐标
    ClientToScreen(hwnd, &ptEnd);
    PatBlt(hdc, ptBeg.x, ptBeg.y, ptEnd.x - ptBeg.x, ptEnd.y - ptBeg.y, DSTINVERT); //DSTINVERT:将目标矩形反向。
    ReleaseDC(hwndSrc, hdc);
}
//复制位图
HBITMAP CopyBitmap(HBITMAP hBitmapSrc)
{
    HBITMAP hBitmapDst;  //目标位图
    BITMAP bitmap;
    HDC    hdcSrc, hdcDst;
    GetObject(hBitmapSrc, sizeof(BITMAP), &bitmap);
    hBitmapDst = CreateBitmapIndirect(&bitmap);
    hdcSrc = CreateCompatibleDC(NULL); //NULL,只是借助桌面DC来复制位图而己
    hdcDst = CreateCompatibleDC(NULL); //NULL,只是借助桌面DC来复制位图而己
    SelectObject(hdcSrc, hBitmapSrc);
    SelectObject(hdcDst, hBitmapDst);
    BitBlt(hdcDst, 0, 0, bitmap.bmWidth, bitmap.bmHeight, hdcSrc, 0, 0, SRCCOPY);
    DeleteDC(hdcDst);
    DeleteDC(hdcSrc);
    return hBitmapDst;  //hBitmapDst是局部变量,但这里是值拷贝,会复制一个
    //hBitmapDst返回
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static BOOL  bCapturing, bBlocking;  //正在鼠标捕获和正在画矩形块
    static POINT ptBeg, ptEnd;
    static HBITMAP hBitmap;
    HBITMAP        hBitmapClip;
    BITMAP bm;
    static HWND   hwndSrc;
    HDC         hdc, hdcMem;
    PAINTSTRUCT ps;
    RECT        rect;
    int         iEnable;
    switch (message)
    {
    case WM_CREATE:

        return 0;
    case WM_INITMENUPOPUP:  //wParam:handle to menu; lparam:item position and indicator
        iEnable = IsClipboardFormatAvailable(CF_BITMAP) ? MF_ENABLED : MF_GRAYED;
        EnableMenuItem((HMENU)(wParam), IDM_EDIT_PASTE, iEnable);

        iEnable = hBitmap ? MF_ENABLED : MF_GRAYED;
        EnableMenuItem((HMENU)(wParam), IDM_EDIT_COPY, iEnable);
        EnableMenuItem((HMENU)(wParam), IDM_EDIT_CUT, iEnable);
        EnableMenuItem((HMENU)(wParam), IDM_EDIT_CLEAR, iEnable);
        return 0;
    case WM_COMMAND:
        switch (LOWORD(wParam)) //菜单ID
        {
        case IDM_EDIT_CUT:
        case IDM_EDIT_COPY:
            if (hBitmap)
            {
                hBitmapClip = CopyBitmap(hBitmap);
                OpenClipboard(hwnd);
                EmptyClipboard();
                SetClipboardData(CF_BITMAP, hBitmapClip);   //位图时,可以是位图句柄,而不用全局句柄
                if (LOWORD(wParam) == IDM_EDIT_COPY)
                    return 0;
                //剪切时,继续执行下云
            }
        case IDM_EDIT_CLEAR:
            if (hBitmap)
            {
                DeleteObject(hBitmap);
                hBitmap = NULL;
            }
            InvalidateRect(hwnd, NULL, TRUE);
            return 0;
        case IDM_EDIT_PASTE:
            if (hBitmap)
            {
                DeleteObject(hBitmap);
                hBitmap = NULL;
            }
            OpenClipboard(hwnd);
            hBitmapClip = GetClipboardData(CF_BITMAP);

            if (hBitmapClip)
            {
                hBitmap = CopyBitmap(hBitmapClip);
            }
            CloseClipboard();
            InvalidateRect(hwnd, NULL, TRUE);
            return 0;
        }
        break;
    case WM_LBUTTONDOWN:
        if (!bCapturing)
        {
            hwndSrc = GetDesktopWindow();
            if (LockWindowUpdate(hwnd))  //锁定屏幕更新
            {
                bCapturing = TRUE;
                SetCapture(hwnd);  //鼠标捕获
                SetCursor(LoadCursor(NULL, IDC_CROSS));
            } else MessageBeep(0);
        }
        return 0;

    case WM_RBUTTONDOWN:
        if (bCapturing)
        {
            bBlocking = TRUE;
            ptBeg.x = LOWORD(lParam);  //这里可以出现负坐标,从而导致截图不正常
            ptBeg.y = HIWORD(lParam);
            ptEnd = ptBeg;
            InvertBlock(hwndSrc, hwnd, ptBeg, ptEnd);
        }
        return 0;
    case WM_MOUSEMOVE:
        if (bBlocking)
        {
            InvertBlock(hwndSrc, hwnd, ptBeg, ptEnd);
            ptEnd.x = LOWORD(lParam);
            ptEnd.y = HIWORD(lParam);
            InvertBlock(hwndSrc, hwnd, ptBeg, ptEnd);
        }
        return 0;
    case WM_LBUTTONUP:
    case WM_RBUTTONUP:
        if (bBlocking)
        {
            InvertBlock(hwndSrc, hwnd, ptBeg, ptEnd);
            ptEnd.x = LOWORD(lParam); //这里可以出现负坐标,从而导致截图不正常
            ptEnd.y = HIWORD(lParam);
            if (hBitmap)
            {
                DeleteObject(hBitmap);
                hBitmap = NULL;
            }
            hdc = GetDC(NULL);  //这里应获取桌面DC,书本这里有误
            ClientToScreen(hwnd, &ptBeg);
            ClientToScreen(hwnd, &ptEnd);
            hdcMem = CreateCompatibleDC(hdc);
            hBitmap = CreateCompatibleBitmap(hdc,
                                             abs(ptEnd.x - ptBeg.x), abs(ptEnd.y - ptBeg.y));
            SelectObject(hdcMem, hBitmap);
            StretchBlt(hdcMem, 0, 0, abs(ptEnd.x - ptBeg.x), abs(ptEnd.y - ptBeg.y),
                       hdc, ptBeg.x, ptBeg.y,
                       ptEnd.x - ptBeg.x, ptEnd.y - ptBeg.y, SRCCOPY);
            DeleteDC(hdcMem);
            DeleteDC(hdc);
            InvalidateRect(hwnd, NULL, TRUE);
        }
        if (bBlocking || bCapturing)
        {
            bBlocking = bCapturing = FALSE;
            SetCursor(LoadCursor(NULL, IDC_ARROW));
            ReleaseCapture();       //释放鼠标捕获
            LockWindowUpdate(NULL);  //屏幕解锁
        }
        return 0;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        if (hBitmap)
        {
            GetClientRect(hwnd, &rect);
            hdcMem = CreateCompatibleDC(hdc);
            SelectObject(hdcMem, hBitmap);
            GetObject(hBitmap, sizeof(BITMAP), &bm);
            //该模式删除所有消除的像素行,不保留其信息
            SetStretchBltMode(hdc, COLORONCOLOR);
            StretchBlt(hdc, 0, 0, rect.right, rect.bottom,
                       hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);

            DeleteDC(hdcMem);
        }

        EndPaint(hwnd, &ps);
        return 0;

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

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 BlowUp.rc 使用
//
#define IDM_EDIT_CUT                    40001
#define IDM_EDIT_COPY                   40002
#define IDM_EDIT_PASTE                  40003
#define IDM_EDIT_CLEAR                  40004
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        103
#define _APS_NEXT_COMMAND_VALUE         40010
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//BlowUp.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""
"
""
END
3 TEXTINCLUDE
BEGIN
"
"
""
END
#endif    // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
BlowUp MENU
BEGIN
POPUP "&Edit"
BEGIN
MENUITEM "Cu&t	Ctrl+X", 40001
MENUITEM "&Copy	Ctrl+C", 40002
MENUITEM "&Paste	Ctrl+V", 40003
MENUITEM "De&lete	Delete", 40004
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
BlowUp ACCELERATORS
BEGIN
"^C", IDM_EDIT_COPY, ASCII, NOINVERT
"^V", IDM_EDIT_PASTE, ASCII, NOINVERT
VK_DELETE, IDM_EDIT_CLEAR, VIRTKEY, NOINVERT
"^X", IDM_EDIT_CUT, ASCII, NOINVERT
END
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
原文地址:https://www.cnblogs.com/5iedu/p/4698507.html