Paul M Watt系列文章要点翻译<一>:Guide to WIN32 Paint for Beginners

来源:互联网 发布:淘宝价格变化 编辑:程序博客网 时间:2024/06/07 10:22

本文英文原版见http://www.codeproject.com/Articles/1988/Guide-to-WIN-Paint-for-Beginners

1.介绍

2.Device Context设备上下文

浅显的说,DC是用来绘制的画布。同时,DC也是操作系统的资源,用来在显示器上绘制图形的画笔、画刷和字体等等均属于DC范畴。DC是Windows系统图形设备接口的抽象层,对于开发者而言,它屏蔽了显示设备的具体实现细节。通过使用并初始化不同的DC,可以使用相同的代码和方法在屏幕、打印机、甚至内存位图上进行绘制。相比于直接操作视频卡、打印机驱动而言,这种方式对于开发者而言更加简洁高效。
DC分为三大类:
  1. Client DC 客户区DC:一个特定的窗口拥有一个客户区DC,对于开发者而言对于目标窗口具有对客户区DC的访问权限。客户区DC对于开发者而言也是使用最为频繁和重要的DC。当产生WM_PAINT消息时,客户区DC就可以获得;
  2. Window DC 窗口DC:一个特定的窗口拥有一个窗口DC,该DC允许开发者绘制窗口的任何部分,包括窗口边框及标题。当WM_NCPAINT消息产生时,窗口DC就可以获得;
  3. Memory DC 内存DC:该DC只存在于内存中,不和窗口绑定。该DC也是唯一一种能操作用户定义位图的DC。内存DC在缓存图像、后台缓冲、复杂图形绘制上极为有效;
  4. General Device DC 一般设备DC:绘图仪、打印机、监视器采用该DC。

3.Obtaining a Client Device Context获得客户区设备上下文

在使用Win32 Paint API时,经常需要获取一个DC句柄。任何一种类型的DC关于其描述均存储在一个HDC中。
对于MFC框架或WTL框架而言,DC的基类是CDC,CDC持有HDC。CPaintDC和CClientDC则继承自CDC类,它们具有各自特殊的用途。

3.1 BeginPaint获取Client DC

BeginPaint是一种普遍的创建DC的方式,但是该函数只能在WM_PAINT消息句柄中使用!这是因为BeginPaint会使窗口无效或过期的区域有效,当某一窗口存在无效区域时,WM_PAINT消息会产生通知窗口重绘此无效区域。如果在WM_PAINT句柄函数之外调用BeginPaint,任何无效的窗口去将变得有效,此时将不会有WM_PAINT消息产生!对于像subclass控件的人而言,存在一些副作用。
此外,当调用BeginPaint时,如果有必要的话Windows会产生WM_ERASEBKGND和WM_NCPAINT消息。如果在WM_PAINT之外调用BeginPaint,你地窗口边框可能不会正常更新。
调用BeginPaint的示例:
PAINTSTRUCT ps;HDC            hdc;hdc = ::BeginPaint(hWnd, &ps);
释放由BeginPaint获得的Client DC的函数是EndPaint。BeginPaint将调用HideCursor将鼠标指针隐藏,EndPaint在结束绘制后将鼠标指针再设成可见。如果在调用BeginPaint后不调用EndPaint将会隐藏鼠标指针。
调用EndPaint的示例:
::EndPaint(hWnd, &ps);
对于MFC和WTL框架而言,Client DC的获取和释放是在CPaintDC类的构造和析构函数中自动完成的。在栈上创建一个CPaintDC对象的实例,该DC也会自动销毁。CPaintDC的构造和析构函数代码如下。
CPaintDC::CPaintDC(CWnd* pWnd){    ...    if (!Attach(::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)))        AfxThrowResourceException();}CPaintDC::~CPaintDC(){    ...    ::EndPaint(m_hWnd, &m_ps);    Detach();}

3.2 GetDC/GetDCEx获取Client DC

另外一种获取客户区DC的方式是通过调用GetDC/GetDCEx。GetDCEx还允许设置一个裁剪区域,这样GetDCEx将不会绘制这些裁剪区域。
GetDC在WM_PAINT句柄函数之外使用很频繁。当需要绘制一些图形时,可以通过GetDC获取DC,这些图形往往不是目标窗口所具有的永久性的图形。比如,在Zoom in和Zoom out操作时用鼠标拉出的像橡皮筋一样的选择区域框,还比如选择一个窗口中的多个对象时的效果,这些图形或效果并不是永久存在的。
通过调用GetDC获得DC和释放DC的一个示例:
HDC hdc;hdc = GetDC(hWnd);...::ReleaseDC(hWnd, hdc);
GetDC和ReleaseDC封装在MFC和WTL框架的CClientDC类中。
CClientDC::CClientDC(CWnd* pWnd){    ...    if (!Attach(::GetDC(m_hWnd = pWnd->GetSafeHwnd())))        AfxThrowResourceException();}CClientDC::~CClientDC(){    ...    ::ReleaseDC(m_hWnd, Detach());}

4.使用DC

使用DC非常方便,下面是采用不同的GDI函数使用DC 的示例:
Rectangle(hdc, 100, 100, 200, 300);Ellipse(hdc, 100, 100, 200, 300);TCHAR szMessage[] = "Paint Beginner";UINT  nLen = _tcslen(szMessage);TextOut(hdc, 100, 325, szMessage, nLen); 
对于CPaintDC而言:
CPaintDC dc;dc.Rectangle(10, 10, 150, 200);
对于ClientDC而言:
CClientDC dc;dc.Rectangle(10, 10, 150, 200);

5.例程

该例子程序使用了两种获取DC的方式
1.BeginPaint:用户在WM_PAINT消息句柄中绘制客户区
2.GetDC:用于绘制鼠标按下左键拖动时的橡皮筋效果
在绘制橡皮筋效果时,采用了DC模式的概念,将DC模式设置为R2_NOT后,第二次相同的绘制操作将会完全清除第一次的绘制操作,从而消除第一次的绘制!

void DrawRubberBand(HWND hWnd){    HDC hdc;            //C: Get a client DC.    hdc = ::GetDC(hWnd);            //C: Set the current drawing mode to XOR, this will allow us            //   to add the rubber band, and later remove it by sending the            //   exact same drawing command.    ::SetROP2(hdc, R2_NOT);            //C: Select a NULL Brush into the DC so that no fill is performed.    ::SelectObject(hdc, ::GetStockObject(NULL_BRUSH));            //C: Get the current shape mode.    HMENU hMenu            = ::GetMenu(hWnd);    HMENU hShapeMenu    = ::GetSubMenu(hMenu, 1);    if (::GetMenuState(hShapeMenu, ID_SHAPE_RECTANGLE, MF_BYCOMMAND) & MF_CHECKED)    {        ::Rectangle(                        hdc,                     ptStart.x,                     ptStart.y,                     ptCurrent.x,                    ptCurrent.y                    );    }    else    {        ::Ellipse(                        hdc,                     ptStart.x,                     ptStart.y,                     ptCurrent.x,                    ptCurrent.y                    );    }            //C: Release the DC.    ::ReleaseDC(hWnd, hdc);}
具体可研究一下代码。
0 0