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

本文英文原版见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);
}
具体可研究一下代码。
Python网络爬虫与推荐算法新闻推荐平台:网络爬虫:通过Python实现新浪新闻的爬取,可爬取新闻页面上的标题、文本、图片、视频链接(保留排版) 推荐算法:权重衰减+标签推荐+区域推荐+热点推荐.zip项目工程资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松复刻,拿到资料包后可轻松复现出一样的项目,本人系统开发经验充足(全领域),有任何使用问题欢迎随时与我联系,我会及时为您解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(如有)等。答辩评审平均分达到96分,放心下载使用!可轻松复现,设计报告也可借鉴此项目,该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的。 【提供帮助】:有任何使用问题欢迎随时与我联系,我会及时解答解惑,提供帮助 【附带帮助】:若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步 【项目价值】:可用在相关项目设计中,皆可应用在项目、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面,可借鉴此优质项目实现复刻,设计报告也可借鉴此项目,也可基于此项目来扩展开发出更多功能 下载后请首先打开README文件(如有),项目工程可直接复现复刻,如果基础还行,也可在此程序基础上进行修改,以实现其它功能。供开源学习/技术交流/学习参考,勿用于商业用途。质量优质,放心下载使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值