当前位置:C++技术网 > 精选软件 > Win32探索实现连续的橡皮擦擦除效果

Win32探索实现连续的橡皮擦擦除效果

更新时间:2015-12-17 22:54:34浏览次数:1+次

    我在文章分析了《SetPixel和GetPixel函数实现自由画图和橡皮擦功能》简单实现了橡皮擦的功能,然后在文章《橡皮擦的标准实现(矩形和圆形橡皮擦)的效率提升:解决擦除不顺畅和提高擦除效率》分析了提高橡皮擦的擦除效率。然而还是没有解决第二个问题。这个问题就是不连续的问题。在画图时我们解决不连续的问题,是用划线代替画点,请阅读《Win32客户区中用画笔自由绘图的功能标准实现》。然而我们现在要思考的问题就是,如何用一种方法来实现连续的?
    相对于用自己画矩形来画橡皮擦矩形,效率比较低的,所以使用矩形画图函数来实现。然而经过测试,其实性能提升还是不够。我们会发现,当鼠标快速滑过之后,矩形还是不连续哦。虽然说对于橡皮擦功能来说,不连续影响不大,但是能够连续是最好了。然而现实很残酷,要实现连续的橡皮擦,也就只能借鉴划线的方法。其实就是将橡皮擦当做划线一样来处理,只是设置的颜色必须是背景颜色,才有橡皮擦的性质。既然如此,能够提高,那就这样实现了。

    在用这个方法之前,我仔细研究了PS的实现,当快速移动之后,不能实时跟进,虽然缓慢的但是还是能够完成比较满意的连续擦除线的。而用擦除背景方式,将橡皮擦范围设置大,然后就出现比较明显的圆形。PS擦除前景的话就很难看到明显的圆形边界。PS的橡皮擦处理效果如下:

    PS擦除背景出现明显的圆形轮廓

PS擦除背景出现明显的圆形轮廓

    PS擦除前景轮廓明显很少了

PS擦除前景轮廓明显很少了
    在这个调查下,我用直接划线的方式,将线条大小设置的很大,用来擦除,就和PS擦除前景一样的效果,而且不会断续,其实就是和划线一个,只是逻辑上将它当做了橡皮擦了。而且实现就和画图的实现是一样的。用直线画图的请参考本文前面提到的文章“自由绘图的功能标准实现”。

    而使用矩形或圆形填充方式来擦除背景的,则始终不能完美达到不断线的擦除。鼠标移动快了就会出现断开的地方没有被擦除。PS也是没有完全实现,只不过优化的很好了,我们就不继续追下去了。如果你有兴趣,可以继续优化实现哦。我们可以看看出现处理的效果:

  以背景色画直线方式来擦除的效果

以背景色画直线方式来擦除的效果

  用填充矩形的方式擦除的效果,快速移动不连续

用填充矩形的方式擦除的效果,快速移动不连续

    用填充圆形的方式擦除的效果,快速移动不连续

用填充圆形的方式擦除的效果,快速移动不连续

    下面是完整的代码:
#include "windows.h"
#include <Windowsx.h>//GET_X_LPARAM,GET_Y_LPARAM宏需要这个头文件
#include <tchar.h>
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    static bool bisPaint=false;
    static bool bisClear=false;
    static bool bisMove=false;
    TCHAR Info[100]=_T("【C++技术网http://www.cjjjs.com】");
    static POINT ptOld;
    switch (message)
    {
     case WM_PAINT:
         hdc = BeginPaint(hwnd,&ps);//会清除无效矩形
         TextOut(hdc,0,10,Info,lstrlen(Info));
         EndPaint(hwnd,&ps);
         return 0;
     case WM_LBUTTONDOWN:
         bisPaint=true;//开始作画
         ptOld.x = GET_X_LPARAM(lParam);
         ptOld.y = GET_Y_LPARAM(lParam);
         return 0;
     case WM_LBUTTONUP:
         bisPaint=false;//松开鼠标,结束绘画
         return 0;
     case WM_RBUTTONDOWN:
         bisClear=true;
         ptOld.x = GET_X_LPARAM(lParam);
         ptOld.y = GET_Y_LPARAM(lParam);
         bisMove=true;
         return 0;
     case WM_RBUTTONUP:
         bisClear=false;
         bisMove=false;
         return 0;
     case WM_KEYDOWN:
         InvalidateRect(hwnd,NULL,TRUE);
         return 0;
     case WM_MOUSEMOVE:
         if(bisClear)
         {
             //-擦除绘图
             hdc = GetDC(hwnd);
             if(bisMove)
             {
                 MoveToEx(hdc,ptOld.x,ptOld.y,NULL);//设置bisMove只需要第一次设置,后面的不需要再设置了
             }

             SelectObject(hdc,CreatePen(PS_SOLID,50,RGB(0,0,0)));
             DeleteObject(GetStockObject(BLACK_PEN));//自己创建的画笔要删除
             LineTo(hdc,ptOld.x=GET_X_LPARAM(lParam),ptOld.y=GET_Y_LPARAM(lParam));
             ptOld.x = GET_X_LPARAM(lParam);
             ptOld.y = GET_Y_LPARAM(lParam);

             ////-用圆形矩形区域填充来实现橡皮擦,提高效率
             //SelectObject(hdc,GetStockObject(BLACK_BRUSH));
             //int size=20;
             ////橡皮擦
             //Rectangle(hdc,GET_X_LPARAM(lParam)-size,GET_Y_LPARAM(lParam)-size,GET_X_LPARAM(lParam)+size,GET_Y_LPARAM(lParam)+size);
             //Ellipse(hdc,GET_X_LPARAM(lParam)-size,GET_Y_LPARAM(lParam)-size,GET_X_LPARAM(lParam)+size,GET_Y_LPARAM(lParam)+size);
             ReleaseDC(hwnd,hdc);
         }else if(bisPaint)//如果按着鼠标时,表示在作画,因此可以开始绘制
         {
             hdc = GetDC(hwnd);
             MoveToEx(hdc,ptOld.x,ptOld.y,NULL);
             SelectObject(hdc,CreatePen(PS_SOLID,50,RGB(255,0,0)));
             LineTo(hdc,GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
             ptOld.x = GET_X_LPARAM(lParam);
             ptOld.y = GET_Y_LPARAM(lParam);
             ReleaseDC(hwnd,hdc);
         }
         return 0;
     case WM_DESTROY:
         PostQuitMessage(0);
         return 0;
     default:
         return DefWindowProc(hwnd, message, wParam, lParam);
    }
}
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrev,LPSTR lpCmd,int iShow)
{
    TCHAR ClassName[] = _T("MyClass");
    TCHAR title1[] = _T("C++技术网http://www.cjjjs.com");
    WNDCLASS wndClass;
    wndClass.cbClsExtra=0;
    wndClass.cbWndExtra=0;
    wndClass.hbrBackground= (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
    wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
    wndClass.hInstance = hInstance;
    wndClass.lpfnWndProc = WinProc;
    wndClass.lpszClassName = ClassName;
    wndClass.lpszMenuName=NULL;
    wndClass.style=CS_HREDRAW|CS_VREDRAW;

    if(!RegisterClass(&wndClass))return 0;

    HWND hwnd = CreateWindow(ClassName,title1,WS_OVERLAPPEDWINDOW,0,0,440,400,NULL,NULL,hInstance,NULL);
    ShowWindow(hwnd,SW_SHOWNORMAL);

    MSG msg;
    while (GetMessage(&msg,NULL,0,0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return msg.wParam;
}