Direct2D 编程入门

例程代码下载

一.引言

早就听说Direct2D和DirectWrite发布了。但由于安装的系统是XP,D2D和DWrite一直没有试——因为D2D/DWrite只支持Win7和Vista SP2。暑假重装了Win7,终于可以用了。如果你学过GDI+或Direct 3D,那么学起来比较容易,因为有很多函数、方法都和GDI+很相似。下面贴一些学习的笔记。

二.开发包安装

有关D2D/DWrite的最新文档,可以在它老窝:http://msdn.microsoft.com/en-us/library/dd370990(VS.85).aspx找到。如果你没有安装D2D/DWrite开发包,可以去http://msdn.microsoft.com/en-us/windowsserver/bb980924.aspx下载最新微软Windows软件开发包。其中有D2D/DWrite所需要的头文件和库文件。安装好之后将Include、Lib目录添加到你的编译器中就可以了。当然你的操作系统必须为Win7或Vista SP2。安装之后的目录结构如下:

 三.初步介绍

D2D开发所需要的头文件有d2d1.h(其中包含了d2d1helper.h、d2dbasetypes.h、d2derr.h),库文件有d2d1.lib。DWrite所需头文件有dwrite.h,库文件为dwrite.lib。D2D根对象为ID2D1Factory和ID2D1Resource接口,它们都是一组COM接口。而ID2D1Resource又通过ID2D1Factory来创建。所有的D2D资源对象都继承自ID2D1Resource接口。

ID2D1Factory接口是D2D程序的起始点。D2D资源有两种:与设备相关(Device Dependent)的和与设备无关(Device InDependent)的。与设备无关的资源在整个应用程序运行过程中一直存在,如ID2D1Factory。而与设备相关的资源当设备丢失(移除)时会停止运作。D2D的绘制工作都交由ID2D1HwndRenderTarget接口来完成,就像GDI+中的Graphics类。它是与设备相关的。其余绘制过程中所用到的资源——如Brush、Bitmap、Mesh、Layer——都由ID2D1HwndRenderTarget接口创建,它们也是设备相关资源。

D2D中的接口一般为ID2D1xxxx,其中xxxx为大小写结合的单词组,如ID2D1SolidBrush。而一般的结构为单词之间用下划线连接的全大写组合(D2D1_XXX_XXX),如D2D1_GRADIENT_STOP。枚举类型则是用下划线连接的大写字母组合,如D2D1_GAMMA;具体的枚举值则是枚举类型后加上限定字符,如D2D1_GAMMA_2_2、D2D1_GAMMA_1_0。函数或方法为大小写结合的单词组,如D2D1CreateFactory。函数一般返回HRESULT值,可用SUCCEEDED(hr)或FAILED(hr)来检测函数调用成功还是失败。

     四.Demo

1> 框架简介:

多说无益,我们看一个例子。创建窗口、消息处理等我们自己写就是了。我们需要关注的是D2D资源的创建、D2D绘制、D2D资源销毁。这样我们需要关注的函数只有下面几个:

BOOL CreateDeviceIndependentResource(); //创建设备无关资源. BOOL CreateDeviceDependentResource(); //创建设备相关资源. void DiscardDeviceIndependentResource(); //销毁设备无关资源. void DiscardDeviceDependentResource(); //销毁设备相关资源. void Render(); //执行D2D绘制. void SizeUpdate(); //WM_SIZE消息触发.

需要注意的是:设备无关资源可以在WinMain入口处就创建。由于涉及到设备丢失的问题,所以设备相关资源必须在创建窗口获得窗口句柄后,窗口显示前完成。Rende()函数可以在程序WM_PAINT消息和空闲时间处理。资源销毁的顺序一般是:先创建的后销毁。所有绘制操作都交由D2D完成,所以我们要处理WM_ERASEBKGND消息。

这样我们需要关注的消息就有:WM_PAINT、WM_ERASEBKGND、WM_SIZE(稍后介绍)。所以程序的一般结构是:

int WINAPI WinMain(…) { CreateDeviceIndependentResource(); //创建设备无关资源. WNDCLASS wndClass; //窗口类填充、注册。 g_hWnd = CreateWindow(…); //创建窗口. CreateDeviceDependentResource(); //创建设备相关资源. ShowWindow(g_hWnd, SW_NORMAL); //显示窗口. UpdateWindow(g_hWnd); //更新窗口. RunMessageLoop(); //抓取消息.GetMessage DiscardDeviceDependentResource(); //销毁设备相关资源. DiscardDeviceIndependentResource(); //销毁设备无关资源. return 0; } // //消息处理回调函数如下: // LRESULT CALLBACK WndProc(HWND hWnd, UINT message, …) { Switch(message) { case WM_PAINT: Render(); //执行D2D绘制. return 0; case WM_ERASEBKGND: return 0; //直接返回,刷新交由D2D处理. case WM_SIZE: SizeUpdate(); //处理WM_SIZE消息. return 0; // 其余消息处理. default: break; } return DefWindowProc(hWnd, message, …); }

一般结构就是这样,如果我们用C++类封装一下,会显得更紧凑。我们要执行D2D绘制,必须定义的接口有ID2D1Factory、ID2D1HwndRenderTarget。如果还要画出一些东西的话,那么就要用到画刷(ID2D1SolidColorBrush等)。

            2> 资源变量定义:

// // 定义的变量. // ID2D1Factory* g_pD2DFactory = 0; //D2D根对象. ID2D1HwndRenderTarget* g_pD2DRenderTarget = 0; //绘制目标. ID2D1SolidColorBrush* g_pSolidBrush = 0; //纯色画刷. ID2D1LinearGradientBrush* g_pLGBrush = 0; //线性渐变画刷. HWND g_hWnd = NULL; //全局窗口句柄.

    3> 资源的创建:

// // D2D设备无关资源的创建. // BOOL CreateDeviceIndependentResource() { HRESULT hr = NULL; hr = D2D1CreateFactory( D2D1_FACTORY_TYPE_SINGLE_THREADED, &g_pD2DFactory ); return SUCCEEDED(hr); }

我们用D2D1CreateFactory创建了一个ID2D1Factory对象,第一个参数很明显是使用单线程。

// // D2D设备相关资源的创建. // BOOL CreateDeviceDependentResource() { HRESULT hr = NULL; RECT rc; ::GetClientRect(g_hWnd, &rc); D2D1_SIZE_U size = SizeU( rc.right - rc.left, rc.bottom - rc.top ); // // 创建绘制目标区域. // hr = g_pD2DFactory->CreateHwndRenderTarget( RenderTargetProperties(), HwndRenderTargetProperties(g_hWnd, size), &g_pD2DRenderTarget ); // // 创建纯色画刷. // if(SUCCEEDED(hr)) { hr = g_pD2DRenderTarget->CreateSolidColorBrush( ColorF(ColorF::OrangeRed), //画刷颜色. &g_pSolidBrush ); } ID2D1GradientStopCollection* pGStop = 0; //渐变点集合. D2D1_GRADIENT_STOP stops[2]; //渐变信息描述数组. stops[0].color = ColorF(ColorF::White); //第一个渐变点颜色. stops[1].color = ColorF(ColorF::Blue); //第二个渐变点颜色. stops[0].position = 0.0f; //第一个渐变点位置百分比. stops[1].position = 1.0f; //第二个渐变点位置百分比. if(SUCCEEDED(hr)) { hr = g_pD2DRenderTarget->CreateGradientStopCollection( stops, sizeof(stops) / sizeof(D2D1_GRADIENT_STOP), D2D1_GAMMA_2_2, //启用Gamma 2.2版本校正. D2D1_EXTEND_MODE_CLAMP, //扩展环绕模式. &pGStop ); } // // 创建线性渐变画刷. // if(SUCCEEDED(hr)) { hr = g_pD2DRenderTarget->CreateLinearGradientBrush( LinearGradientBrushProperties( Point2F(0.0f, 0.0f) //第一个渐变点坐标. Point2F(1.0f, 1.0f) //第二个渐变点坐标. ), pGStop, //渐变信息集合. &g_pLGBrush //所创建的渐变画刷. ); SafeRelease(pGStop); //释放局部资源. } return SUCCEEDED(hr); }

我们在释放的时候用到了一个模板SafeRelease,我们的定义如下:

// // 释放模板. // template<typename Type> void SafeRelease(Type& pObjToRelease) { if(pObjToRelease) { pObjToRelease->Release(); pObjToRelease = 0; } }

  4> D2D绘制:

很简单。我们接着看绘制Render():

// // D2D绘制函数. // void Render() { // // 如果设备丢失,则创建与设备相关资源. // if(!g_pD2DRenderTarget) { if(!CreateDeviceDependentResource()) { return ; } } g_pD2DRenderTarget->BeginDraw(); //开始绘制. g_pD2DRenderTarget->Clear(ColorF(ColorF::White)); //将窗体设置为白色. // // 执行D2D绘制. // D2D1_POINT_2F StartPoint = Point2F(10.0f, 10.0f); D2D1_POINT_2F EndPoint = Point2F(100.0f, 100.0f); g_pSolidBrush->SetColor(ColorF(ColorF::Red)); //设置纯色画刷颜色. g_pD2DRenderTarget->DrawLine( //绘制一条线段. StartPoint, //起点. EndPoint, //终点. g_pSolidBrush, //所使用的画刷. 8.0f //线宽. ); D2D1_RECT_F rc = RectF(130.0f, 10.0f, 230.0f, 110.0f); g_pSolidBrush->SetColor(ColorF(ColorF::Pink)); g_pD2DRenderTarget->DrawRectangle( //绘制一个矩形. &rc, //要绘制的矩形. g_pSolidBrush, //所使用画刷. 6.0f //线宽. ); // // 用线性渐变画刷绘制一个椭圆. // D2D1_ELLIPSE ellipse = Ellipse( Point2F(350.0f, 70.0f), 80.0f, 40.0f ); g_pLGBrush->SetStartPoint(Point2F(270.0f, 30.0f)); //设置渐变起点. g_pLGBrush->SetEndPoint(Point2F(430.0f, 110.0f)); //设置渐变终点. g_pD2DRenderTarget->DrawEllipse( //绘制椭圆. ellipse, g_pLGBrush, 20.0f ); // // 用线性渐变画刷填充一个矩形. // g_pLGBrush->SetStartPoint(Point2F(10.0f, 150.0f)); //设置渐变起点. g_pLGBrush->SetEndPoint(Point2F(160.0f, 300.0f)); //设置渐变终点. rc = RectF(10.0f, 150.0f, 160.0f, 300.0f); g_pD2DRenderTarget->FillRectangle( rc, g_pLGBrush ); // // 绘制一个绿色圆角矩形. // rc = RectF(200.0f, 150.0f, 450.0f, 300.0f); D2D1_ROUNDED_RECT RoundRc = RoundedRect(rc, 10.0f, 10.0f); g_pSolidBrush->SetColor(ColorF(ColorF::ForestGreen)); g_pD2DRenderTarget->DrawRoundedRectangle( &RoundRc, g_pSolidBrush, 2.0f ); HRESULT hr = g_pD2DRenderTarget->EndDraw(); // // 如果设备丢失.我们丢弃设备相关资源以备下次 // 执行绘制时创建. // if(D2DERR_RECREATE_TARGET == hr) { DiscardDeviceDependentResource(); } }

如果你学过GDI+,可以看到,所有的绘制步骤与GDI+极其类似。绘制也很简单,三步走:开始绘制(BeginDraw)、绘制、结束绘制(EndDraw)。期间我们用ID2D1HwndRenderTarget::Clear来将窗体背景设置为我们想要的颜色——此例中我们设置为白色ColorFul::White。可以看出,全部的绘制工作都交由ID2D1HwndRenderTarget对象来完成。

除了绘制,我们还要处理设备丢失的问题——如果设备丢失,则丢弃与设备相关的资源,因为它们已经不可用了。然后在下一次绘制开始时重新创建设备相关资源。而设备无关资源一旦创建,就在整个应用程序运作过程中存在。

   5> 资源释放:

我们接着看资源的释放。由于我们已经介绍了SafeRelease这个模板,所以释放工作显得非常简单。我们只需遵循“先创建的后释放”这一原则就是了。

// // 销毁资源无关资源. // void DiscardDeviceIndependentResource() { SafeRelease(g_pD2DFactory); } // // 销毁资源相关资源. // void DiscardDeviceDependentResource() { SafeRelease(g_pLGBrush); SafeRelease(g_pSolidBrush); SafeRelease(g_pD2DRenderTarget); }

   6> WM_SIZE消息处理:

我们一直提到了WM_SIZE消息的处理。这有什么用呢?我们先看CreateDeviceDependentResource函数中ID2D1Factory::CreateHwndRenderTarget的第二个参数HwndRenderTargetProperties(g_hWnd, size);其中g_hWnd是这个绘制目标所关联的窗口句柄,而size是一个D2D1_SIZE_U类型的结构。size我们用的是客户区(Client)的宽度和高度来初始化的。所以,ID2D1HwndRenderTarget不仅关联了要绘制窗口的句柄,而且还关联了要绘制区域的大小。当窗口大小发生改变时,我们必须重设这个绘制区域大小。就是下面的SizeUpdate函数:

// // 处理WM_SIZE消息. // void SizeUpdate() { RECT rc; GetClientRect(g_hWnd, &rc); g_pD2DRenderTarget->Resize( SizeU(rc.right - rc.left, rc.bottom - rc.top) ); }

如果你在SizeUpdate中不做任何事情,那么会出现什么情况呢?如果你注释SizeUpdate中的所有代码,当你运行例程时,会发生有趣的现象:当拖动改变窗体大小时,窗体上你所绘制的图形也会随之变大或缩小!!你可以自己试验一下。

 

7> 运行与分析:

编译链接成功后,我们可以看看运行结果了。如下:

我同样用GDI+实现过这样的程序,但如果不用双缓冲的话,GDI+绘制的程序当拖动时有明显的闪烁现象。而此D2D绘制的例程在拉动、拖动时没有任何闪烁。D2D比GDI+的强大之处可见一斑。

五.小结

我们通过初步介绍D2D,并用一个小例程来学习了如何使用D2D来做自己想做的事情——当然,这个程序不是太酷,但这都是最基本的东西。只有将基础的东西弄懂,才能去发掘D2D中最精妙的知识。在附件中,包含了本例程的所有代码和可执行程序。当然,为了使程序显得更紧凑,我另外写了一个CD2DDemoApp类来封装了各个函数,也放在了附件中。你可以用VS2008或者VS2010编译。

【转载请注明出处】

Direct2D编程入门相关推荐

  1. python编程求圆的面积案例_Python实用案例编程入门:第七章 调式手段

    本章的主题为调试手段,这是程序开发必不可少的步骤,也是占用时间最多的环节.在程序员的正常开发工作中,调试工作至少占据1/3的时间,而实际编码工作相对占用实际比较少.因此,无论您是初学者,还是编程兴趣爱 ...

  2. 《C++游戏编程入门(第4版)》——1.12 习题

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.1节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  3. 编程入门到进大厂,你需要这套学习架构

    我相信大多数学习编程的同学都有着对大公司的憧憬.技术.声望.薪资.福利,这些都足以成为吸引你进入大厂的理由. 但是,如何进入大厂呢? 对于很多同学来说,通往大厂的道路并不明朗,不知道是否有希望,也不知 ...

  4. 《C++游戏编程入门(第4版)》——1.8 Lost Fortune简介

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第1章,第1.8节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  5. [译]函数式响应编程入门指南

    原文地址:An Introduction to Functional Reactive Programming 原文作者:Daniel Lew 译文出自:掘金翻译计划 本文永久链接:github.co ...

  6. 《C++游戏编程入门(第4版)》——2.4 使用带else子句的if语句序列

    本节书摘来自异步社区出版社<C++游戏编程入门(第4版)>一书中的第2章,第2.4节,作者:[美]Michael Dawson(道森),更多章节内容可以访问云栖社区"异步社区&q ...

  7. 《树莓派Python编程入门与实战》——3.5 关于Python交互式shell

    本节书摘来异步社区<树莓派Python编程入门与实战>一书中的第3章,第3.5节,作者:[美]Richard Blum,更多章节内容可以访问云栖社区"异步社区"公众号查 ...

  8. 编程指南_今晚7点,译者编程入门指南抽奖!

    各位关注"简言"的同学们好.老师们! 我的新书<译者编程入门指南>出版啦!感谢大家一直以来的支持和陪伴,我每次发完文章后都会得到大家的点赞.转发.留言甚至打赏,我感到非 ...

  9. 《树莓派Python编程入门与实战(第2版)》——3.9 小结

    本节书摘来自异步社区<树莓派Python编程入门与实战(第2版)>一书中的第3章,第3.9节,作者[美] Richard Blum Christine Bresnahan,陈晓明 马立新 ...

最新文章

  1. 28个HTML5特征、窍门和技术
  2. python创建一个txt文件-python中如何创建一个txt文件
  3. [java进阶]4.关键字throws和throw
  4. js利用tab键切换当前页面_JS实现的tab切换并显示相应内容模块功能示例
  5. 使用Thumbnailator压缩照片
  6. 测试工具之badboy
  7. 使用 CXF 做 webservice 简单例子
  8. 【Pytorch神经网络基础理论篇】 04 线性代数
  9. 每周工作量及时间统计
  10. Sql Server约束的学习一(主键约束、外键约束、唯一约束)
  11. java中简单的打字游戏_[Java教程]jQuery 写的简单打字游戏
  12. 笔记本电脑java记事本在哪_如何打开电脑记事本_电脑记事本在哪
  13. python qt5 安装
  14. 满足AUTOSAR基础软件要求的硬件安全模块(HSM)加密运算
  15. 别再售卖 5块钱 的 Win10 激活码了,后果很严重
  16. 【Java】运用泽勒一致性计算某天是星期几
  17. python实现触摸精灵功能_触摸精灵lua脚本实现微信群加好友功能
  18. 飞机实时动态查询接口代码调用服务
  19. Android Studio掷骰子生成随机数(Java)
  20. 微信小程序,大佬救我!!!

热门文章

  1. 前置机应用服务器,web服务器前置机(erp)部署步骤.pdf
  2. 使用KVM创建虚拟机
  3. OAuth2实现单点登录SSO
  4. 基于jsp+java+ssm的农产品购物商城系统-计算机毕业设计
  5. 中视典编辑器 输出html5,虚拟现实软件|VRP-BUILDER 虚拟现实编辑器 - 【中视典数字科技】...
  6. 不同场所最低疏散净宽度汇总
  7. linux查看指定目录下各个文件大小以及总体大小
  8. 沁恒CH552G实现最小系统[沁恒8位机MCU最小系统]
  9. Arduino使用TM1637 4位数码管模块
  10. Could not resolve placeholder ‘project.version‘ in value “${project.version}”