当前位置:C++技术网 > 资讯 > SaveDC和RestoreDC的状态栈使用详细分析

SaveDC和RestoreDC的状态栈使用详细分析

更新时间:2015-12-15 22:27:52浏览次数:1+次

    当我们需要反复的绘制图形时,将之前的DC状态保存起来,等当前的绘制完毕之后,可以再恢复到之前的状态。然后就可以接着之前的状态绘制了。
    不仅如此,保存和恢复DC状态,可以保存很多次,而不是只能保存一次。并且这个状态栈系统自动维护,不需要程序员来处理。我们只需要知道这个模型,知道使用SaveDC保存状态和RestoreDC恢复状态即可。
    我们先来看看栈的模型。这个栈的模型就是标准的栈模型,先进后出。那么最先保存的状态就存放在栈底,最后保存的状态就是在最上面。那么如果想恢复栈顶以下任何一个状态,则需要先闪出去这个指定状态以上的所有状态,这样才能得到这个状态数据进而恢复状态。
    好了,我们来看看这两个函数介绍。函数使用太简单了,简单介绍一下。两个函数声明如下:
int SaveDC(HDC hdc);
BOOL RestoreDC(HDC hdc,int nSavedDC);
    hdc参数自然就是设备环境DC的句柄咯。这个DC句柄来源于各种获取DC的函数,比如BeginPaint、GetDC等。SaveDC的返回值就是指示状态的一个整数值。这个整数值就是RestoreDC函数第二个参数需要传入的。这样我们将这个整数值传入RestoreDC函数后,就可以将当前的DC状态恢复到这个整数对应的状态。
    这里需要注意一下,SavedDC函数返回的值是从1开始计数的,所以第一个保存的值就是1,第二个保存的值是2.当然不建议这么做,在恢复时请使用保存的值来恢复。这样永远不会出错,万一什么时候API在哪个系统版本不一样,返回的值并不是从1开始的呢,那不是错位了。
    还有一点,如果nSavedDC的值是负数,如何理解呢?如果是正数自然就是指定的状态值去找到栈中的那个状态,然后恢复并将这个状态上面的状态都删掉。而负数的值则会让系统从栈顶往栈底方向数,-1就是最顶上的这个状态,当前状态在栈顶的上一个位置。那么-2则是栈顶的下一个位置。如果超越了,则始终只取到栈底那一个状态,这个是无法删除的。因为DC无论如何有一个状态的。
    下面来看代码:
TextOut(hdc,0,20*iY++,_T("开始绘制,准备保存DC"),lstrlen(_T("开始绘制,准备保存DC")));
SetTextColor(hdc,RGB(255,0,0));
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
iRed = SaveDC(hdc);//-保存一次
SetTextColor(hdc,RGB(0,255,0));
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
iGreen = SaveDC(hdc);//-保存第二次
SetTextColor(hdc,RGB(0,0,255));
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
iBlue = SaveDC(hdc);//-保存第三次
    这样保存了三次之后,效果图如下:
   
    然后我们用-1来依次从栈顶到栈底来恢复,代码如下:
TextOut(hdc,0,20*iY++,_T("开始恢复DC绘制"),lstrlen(_T("开始恢复DC绘制")));
//-依次从上到下来恢复
RestoreDC(hdc,-1);//-恢复一次
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
RestoreDC(hdc,-1);//-恢复第二次
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
RestoreDC(hdc,-1);//-恢复第三次
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
    执行后的栈的情况和窗口效果如下:
   
    然后我们知道,栈顶指针已经到最下了。不管你是用那个状态值来恢复,始终只能得到红色的DC了。代码如下:
TextOut(hdc,0,20*iY++,_T("最后试图被删掉的DC"),lstrlen(_T("最后试图被删掉的DC")));
RestoreDC(hdc,iGreen);
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
RestoreDC(hdc,iBlue);
TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
    执行后,栈顶指针没有变化,窗口效果图如下:
   
    相信有了原理解释和代码分步讲解,你应该对这个技术点不再陌生了吧。应该说是很清楚了。下面给出完整的程序代码:
#include "windows.h"
#include <tchar.h>
// - 项目是Unicode字符集
LRESULT CALLBACK WinProc(HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
    HDC hdc;
    PAINTSTRUCT ps;
    int iY=0;
    TCHAR Info[100]=_T("C++技术网http://www.cjjjs.com");
    int iRed,iGreen,iBlue;
    switch (message)
    {
     case WM_PAINT:
         hdc = BeginPaint(hwnd,&ps);//会清除无效矩形
         
        TextOut(hdc,0,20*iY++,_T("开始绘制,准备保存DC"),lstrlen(_T("开始绘制,准备保存DC")));
        SetTextColor(hdc,RGB(255,0,0));
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        iRed = SaveDC(hdc);//-保存一次
        SetTextColor(hdc,RGB(0,255,0));
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        iGreen = SaveDC(hdc);//-保存第二次
        SetTextColor(hdc,RGB(0,0,255));
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        iBlue = SaveDC(hdc);//-保存第三次

        TextOut(hdc,0,20*iY++,_T("开始恢复DC绘制"),lstrlen(_T("开始恢复DC绘制")));
        //-依次从上到下来恢复
        RestoreDC(hdc,-1);//-恢复一次
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        RestoreDC(hdc,-1);//-恢复第二次
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        RestoreDC(hdc,-1);//-恢复第三次
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));


        TextOut(hdc,0,20*iY++,_T("最后试图被删掉的DC"),lstrlen(_T("最后试图被删掉的DC")));
        RestoreDC(hdc,iGreen);
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));
        RestoreDC(hdc,iBlue);
        TextOut(hdc,0,20*iY++,Info,lstrlen(Info));

         EndPaint(hwnd,&ps);
         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(GRAY_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;
}