zoukankan      html  css  js  c++  java
  • 第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
  • 相关阅读:
    状态线程
    C++编译 C # 调用方法
    图像算法集合
    openmp 和 thread 性能实测
    RGB转YUV 各种库的性能比较
    ipp 实现图像空间的转换
    Eigen 学习笔记
    线性代数笔记
    凸优化 笔记
    Windows系统服务器中安装Redis服务
  • 原文地址:https://www.cnblogs.com/5iedu/p/4698507.html
Copyright © 2011-2022 走看看