• MFC和Win32之三___CGdiObject类和windows Gdi对象


    小结:

    前面讲到的windows窗口对象,在windows下用句柄来代表之,并且用了一个数据结构WNDCLASS(窗口类)来描述之。同理,windows的Gdi对象也有一些句柄来代表之(比如hPen等),同时也有一个数据结构来描述之,即设备描述表(Device Context),而且其本身也有句柄,因此也可以看作是windows的一个对象。

    由于设备描述表这个数据结果不像WNDCLASS那样单一,因为他要描述多个绘图设备。所以MFC同时也封装设备描述表,并用几个类来代表不同的设备描述表,他们的基类都是CDC。这里特别讲到了,当直接使用CDC这个基类的情况,可以看出CDC这个基类的构造函数中并没有创建任何的设备描述表,所以要使用时创建一个设备描述表(堆上,栈上都可以)。同时这个基类的析构函数不是虚拟的,所以不必担心该对象释放时,它的句柄没有释放。

    windows的Gdi对象,就是通过CGdiObject来封装。


     

      1. 设备描述表

         

        1. 设备描述表概述

    当一个应用程序使用GDI函数时,必须先装入特定的设备驱动程序,然后为绘制窗口准备设备描述表,比如指定线的宽度和颜色、刷子的样式和颜色、字体、剪裁区域等等。不像其他Win32结构,设备描述表不能被直接访问,只能通过系列Win32函数来间接地操作。

    如同Windows“窗口类”一样,设备描述表也是一种Windows数据结构,用来描述绘制窗口所需要的信息。它定义了一个坐标映射模式、一组GDI图形对象及其属性。这些GDI对象包括用于画线的笔,绘图、填图的刷子,位图,调色板,剪裁区域,及路径(Path)。

    表2-2列出了设备描述表的结构和各项缺省值,表2-3列出了设备描述表的类型,表2-4显示设备描述表的类型。

    表2-2 设备描述表的结构

    属性

    缺省值

    Background color

    Background color setting from Windows Control Panel (typically, white)

    Background mode

    OPAQUE

    Bitmap

    None

    Brush

    WHITE_BRUSH

    Brush origin

    (0,0)

    Clipping region

    Entire window or client area with the update region clipped, as appropriate. Child and pop-up windows in the client area may also be clipped

    Palette

    DEFAULT_PALETTE

    Current pen position

    (0,0)

    Device origin

    Upper left corner of the window or the client area

    Drawing mode

    R2_COPYPEN

    Font

    SYSTEM_FONT (SYSTEM_FIXED_FONT for applications written to run with Windows versions 3.0 and earlier)

    Intercharacter spacing

    0

    Mapping mode

    MM_TEXT

    Pen

    BLACK_PEN

    Polygon-fill mode

    ALTERNATE

    Stretch mode

    BLACKONWHITE

    Text color

    Text color setting from Control Panel (typically, black)

    Viewport extent

    (1,1)

    Viewport origin

    (0,0)

    Window extent

    (1,1)

    Window origin

    (0,0)

     

    表2-3 设备描述表的分类

    Display

    显示设备描述表,提供对视频显示设备上的绘制操作的支持

    Printer

    打印设备描述表,提供对打印机、绘图仪设备上的绘制操作的支持

    Memory

    内存设备描述表,提供对位图操作的支持

    Information

    信息设备描述表,提供对操作设备信息获取的支持

    表2-3中的显示设备描述表又分三种类型,如表2-4所示。

    表2-4 显示设备描述表的分类

    名称

    特点

    功能

    Class Device

    Contexts

    提供对Win16的向后兼容

     

    Common

    Device

    Contexts

    在Windows系统的高速缓冲区,数量有限

    Applicaion获取设备描述表时,Windows用缺省值初始化该设备描述表,Application使用它完成绘制操作,然后释放

    Private

    Device

    Contexts

    没有数量限制,用完不需释放一次获取,多次使用

    多次使用过程中,每次设备描述表属性的任何修改或变化都会被保存,以支持快速绘制

     

    (1)使用设备描述表的步骤

    要使用设备描述表,一般有如下步骤:

     

    • 获取或者创建设备描述表;

       

    • 必要的话,改变设备描述表的属性;

       

    • 使用设备描述表完成绘制操作;

       

    • 释放或删除设备描述表。

    Common设备描述表通过::GetDC,::GetDCEx,::BeginPaint来获得一个设备描述表,用毕,用::ReleaseDC或::EndPaint释放设备描述表;

    Printer设备描述表通过::CreateDC创建设备描述表,用::DeleteDC删除设备描述表。

    Memory设备描述表通过::CreateCompatibleDC创建设备描述表,用::DeleteDC删除。

    Information设备描述表通过::CreateIC创建设备描述表,用::DeleteDC删除。

    (2)改变设备描述表属性的途径

    要改变设备描述表的属性,可通过以下途径:

    用::SelectObject选入新的除调色板以外的GDI Object到设备描述表中;

    对于调色板,使用::SelectPalette函数选入逻辑调色板,并使用::RealizePalette把逻辑调色板的入口映射到物理调色板中。

    用其他API函数改变其他属性,如::SetMapMode改变映射模式。

     

        1. 设备描述表在MFC中的实现

    MFC提供了CDC类作为设备描述表类的基类,它封装了Windows的HDC设备描述表对象和相关函数。

     

    1. CDC类

      CDC类包含了各种类型的Windows设备描述表的全部功能,封装了所有的Win32 GDI 函数和设备描述表相关的SDK函数。在MFC下,使用CDC的成员函数来完成所有的窗口绘制工作。

      CDC 类的结构示意图2-2所示。

       

      CDC类有两个成员变量:m_hDC,m_hAttribDC,它们都是Windows设备描述表句柄。CDC的成员函数作输出操作时,使用m_Hdc;要获取设备描述表的属性时,使用m_hAttribDC。

      在创建一个CDC类实例时,缺省的m_hDC等于m_hAttribDC。如果需要的话,程序员可以分别指定它们。例如,MFC框架实现CMetaFileDC类时,就是如此:CMetaFileDC从物理设备上读取设备信息,输出则送到元文件(metafile)上,所以m_hDC和m_hAttribDC是不同的,各司其责。还有一个类似的例子:打印预览的实现,一个代表打印机模拟输出,一个代表屏幕显示。

      CDC封装::SelectObject(HDC hdc,HGDIOBJECT hgdiobject)函数时,采用了重载技术,即它针对不同的GDI对象,提供了名同而参数不同的成员函数:

      SelectObject(CPen *pen)用于选入笔;

      SelectObject(CBitmap* pBitmap)用于选入位图;

      SelectObject(CRgn *pRgn)用于选入剪裁区域;

      SelectObject(CBrush *pBrush)用于选入刷子;

      SelectObject(CFont *pFont)用于选入字体;

      至于调色板,使用SelectPalette(CPalette *pPalette,BOOL bForceBackground )选入调色板到设备描述表,使用RealizePalletter()实现逻辑调色板到物理调色板的映射。

       

    2. 从CDC派生出功能更具体的设备描述表

    从CDC 派生出四个功能更具体的设备描述表类。层次如图2-3所示。

     

     

    下面,分别讨论派生出的四种设备描述表。

     

    • CCientDC

    代表窗口客户区的设备描述表。其构造函数CClientDC(CWnd *pWin)通过::GetDC获取指定窗口的客户区的设备描述表HDC,并且使用成员函数Attach把它和CClientDC对象捆绑在一起;其析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用::ReleaseDC释放设备描述表HDC。

     

    • CPaintDC

    仅仅用于响应WM_PAINT消息时绘制窗口,因为它的构造函数调用了::BeginPaint获取设备描述表HDC,并且使用成员函数Attach把它和CPaintDC对象捆绑在一起;析构函数使用成员函数Detach把设备描述表句柄HDC分离出来,并调用::EndPaint释放设备描述表HDC,而::BeginPaint和::EndPaint仅仅在响应WM_PAINT时使用。

     

    • CMetaFileDC

    用于生成元文件。

     

    • CWindowDC

    代表整个窗口区(包括非客户区)的设备描述表。其构造函数CWindowDC(CWnd *pWin)通过::GetWindowDC获取指定窗口的客户区的设备描述表HDC,并使用Attach把它和CWindowDC对象捆绑在一起;其析构函数使用Detach把设备描述表HDC分离出来,调用::ReleaseDC释放设备描述表HDC。

     

        1. MFC设备描述表类的使用

     

    1. 使用CPaintDC、CClientDC、CWindowDC的方法

      首先,定义一个这些类的实例变量,通常在栈中定义。然后,使用它。

      例如,MFC中CView对WM_PAINT消息的实现方法如下:

      void CView::OnPaint()

      {

      // standard paint routine

      CPaintDC dc(this);

      OnPrepareDC(&dc);

      OnDraw(&dc);

      }

      在栈中定义了CPaintDC类型的变量dc,随着构造函数的调用获取了设备描述表;设备描述表使用完毕,超出其有效范围就被自动地清除,随着析构函数的调用,其获取的设备描述表被释放。

      如果希望在堆中创建,例如

      CPaintDC *pDC;

      pDC = new CPaintDC(this)

      则在使用完毕时,用delete删除pDC:

      delete pDC;

       

       

    2. 直接使用CDC

    需要注意的是:在生成CDC对象的时候,并不像它的派生类那样,在构造函数里获取相应的Windows设备描述表。最好不要使用::GetDC等函数来获取一个设备描述表,而是创建一个设备描述表。其构造函数如下:

    CDC::CDC()

    {

    m_hDC = NULL;

    m_hAttribDC = NULL;

    m_bPrinting = FALSE;

    }

    其析构函数如下:

    CDC::~CDC()

    {

    if (m_hDC != NULL)

    ::DeleteDC(Detach());

    }

    在CDC析构函数中,如果设备描述表句柄不空,则调用DeleteDC删除它。这是直接使用CDC时最好创建Windows设备描述表的理由。如果设备描述表不是创建的,则应该在析构函数被调用前分离出设备描述表句柄并用::RealeaseDC释放它,释放后m_hDC为空,则在析构函数调用时不会执行::DeleteDC。当然,不用担心CDC的派生类的析构函数调用CDC的析构函数,因为CDC::~CDC()不是虚拟析构函数。

    直接使用CDC的例子是内存设备上下文,例如:

    CDC dcMem; //声明一个CDC对象

    dcMem.CreateCompatibleDC(&dc); //创建设备描述表

    pbmOld = dcMem.SelectObject(&m_bmBall);//更改设备描述表属性

    …//作一些绘制操作

     

    dcMem.SelectObject(pbmOld);//恢复设备描述表的属性

    dcMem.DeleteDC(); //可以不调用,而让析构函数去删除设备描述表

     

      1. GDI对象

    在讨论设备描述表时,已经多次涉及到GDI对象。这里,需强调一下:GDI对象要选入Windows 设备描述表后才能使用;用毕,要恢复设备描述表的原GDI对象,并删除该GDI对象。

    一般按如下步骤使用GDI对象:

    Create or get a GDI OBJECT hNewGdi;

     

    hOldGdi = ::SelectObject(hdc, hNewGdi)

    ……

    ::SelectObject(hdc, hOldGdi)

    ::DeleteObject(hNewGdi)

    先创建或得到一个GDI对象,然后把它选入设备描述表并保存它原来的GDI对象;用毕恢复设备描述表原来的GDI对象并删除新创建的GDI对象。

    需要指出的是,如果hNewGdi是一个Stock GDI对象,可以不删除(删除也可以)。通过

    HGDIOBJ GetStockObject(

    int fnObject // type of stock object

    );

    来获取Stock GDI对象。

     

     

    1. MFC GDI对象

      MFC用一些类封装了Windows GDI对象和相关函数,层次结构如图2-4所示:

       

       

      CGdiObject封装了Windows GDI Object共有的特性。其派生类在继承的基础上,主要封装了各类GDI的创建函数以及和具体GDI对象相关的操作。

       

      CGdiObject的构造函数仅仅让m_hObject为空。如果m_hObject不空,其析构函数将删除对应的Windows GDI对象。MFC GDI对象和Windows GDI对象的关系如图2-5所示。

       

    2. 使用MFC GDI类的使用

    首先创建GDI对象,可分一步或两步创建。一步创建就是构造MFC对象和Windows GDI对象一步完成;两步创建则先构造MFC对象,接着创建Windows GDI对象。然后,把新创建的GDI对象选进设备描述表,取代原GDI对象并保存。最后,恢复原GDI对象。例如:

    void CMyView::OnDraw(CDC *pDC)

    {

    CPen penBlack; //构造MFC CPen对象

    if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))

    {

    CPen *pOldPen = pDC->SelectObject(&penBlack)); //选进设备表,保存原笔

    pDC->SelectObject(pOldPen); //恢复原笔

    }else

    {

    }

    }

    和在SDK下有一点不同的是:这里没有DeleteObject。因为执行完OnDraw后,栈中的penBlack被销毁,它的析构函数被调用,导致DeleteObject的调用。

    还有一点要说明:

    pDC->SelectObject(&penBlack)返回了一个CPen *指针,也就是说,它根据原来PEN的句柄创建了一个MFC CPen对象。这个对象是否需要删除呢?不必要,因为它是一个临时对象,MFC框架会自动地删除它。当然,在本函数执行完毕把控制权返回给主消息循环之前,该对象是有效的。

  • 相关阅读:
    【2020-02-13】内容变了,但计划本身没变
    【2020-02-12】新的工作计划方式
    【2020-02-10】煮饭那点家常
    【2020-02-10】生活需要不断地相互协调
    nacicat premium 快捷键
    python 爬虫之 正则的一些小例子
    Python爬虫之Cookie和Session
    Python爬虫之关于登录那些事
    爬虫常用库之pyquery 库
    day 39 jq 学习入门2
  • 原文地址:https://www.cnblogs.com/ghw0501/p/4733925.html
Copyright © 2020-2023  润新知