引子

任何一门语言的第一个教程几乎都是Hello,world。我们也不例外,但是这里不是教大家打印Hello,world,而是编写一个简单的D2D绘制程序,让大家对Direct2D的程序结构及编程方法有一个基本的认识。下面我们来看如何一步一步绘制一个矩形。

基本概念

在开始之前,还是先介绍一些基本的概念,有助于大家理解程序,这些概念包括,Brush(画刷),Render target(渲染目标),Geometry(几何图形),它们会贯穿整个教程,所以越早介绍越好,对于有Windows GDI基础的人来说,理解这些概念很容易。没有基础的也没关系,我们可以先了解一下,随着学习的深入,会有更加深刻的认识。

Brush

Brush-画刷,画刷是绘图的工具,它管理图形的颜色,虚实,画刷可以绘制几何图形,也可以绘制位图。

Render target

Render target-渲染目标(姑且这么翻译吧)是绘制的场所,其实这就是一个surface,一个表面,再具体点就是一块内存,可以是显存,也可能是系统内存。所有的绘图操作都在这里完成。

Geometry, Bitmap, Text

Geometry-几何图形,Bitmap-位图, Text-文本。这三者是要绘制的内容,几何图形包括矩形,圆角矩形,椭圆等,当然Direct2D除了可以绘制几何图形之外,还可绘制位图和文本,D2D没有提供加载位图的接口,所以对位图的加载都是使用WIC来实现的。而对文本的绘制则是通过DirectWrite来实现的,DirectWrite在分类上属于D2D,但是目前已经独立成一个组件了。为了便于理解以上三者之间的关系,大家可以想象一个画家,他在作画的时候,都需要那些东西呢?第一,他需要一支画笔用来绘制,这相当于上面的画刷,第二,他需要一张纸或者一张画布用来承载绘制的东西,这就是上面的Render target,最后,他想画什么呢?山水?花鸟?亦或是人物?这就是绘制的内容,相当于上面的几何图形,位图或者文本。

Resource

在Direct2D中主要有两种资源,一是设备无关的资源,另一个是设备相关的资源。所谓设备无关,是指该资源不与特定的硬件渲染设备相关联,所以设备无关的资源都分配在CPU中,而设备相关是指该资源与特定的渲染硬件相关联,比如当硬件加速可用时,使用GPU渲染,否则使用CPU渲染。

设备无关的资源

  • ID2D1DrawingStateBlock
  • ID2D1Factory
  • ID2D1Geometry及继承自它的接口
  • ID2D1GeometrySink和ID2D1SimplifiedGeometrySink
  • ID2D1StrokeStyle

除了ID2D1RenderTarget之外,所有使用ID2D1Factory创建的资源都是设备无关的。

设备相关的资源

  • ID2D1Brush及继承自它的接口
  • ID2D1Layer
  • ID2D1RenderTarget及继承自它的接口

一般来说,使用ID2D1RenderTarget创建的资源都是设备相关的。

程序框架

一个简单的D2D程序大致包含下面三个核心函数。

1 创建资源(CreateResources) - 设备无关的资源可以一次性创建,永久使用,而设备相关的资源则需要随着设备改变而相应的改变。

2 渲染(Render) - 响应WM_PAINT消息进行绘制。

3 清理资源(Cleanup) - DX是基于COM的,所有COM对象在使用完毕时,都要释放。

为了便于大家理解,我画了一张图,这个图简单描述了D2D程序的基本渲染流程。

需要说明的是,Direct2D的渲染时机与D3D有些不同,D3D是在没有消息处理时进行渲染,而D2D则是响应WM_PAINT消息进行渲染。下面的代码中会有详细的解释。

代码

添加头文件

除了Win32编程需要的头文件(比如Windows.h)之外,任何D2D程序都需要头文件d2d1.h。

#include <windows.h>
#include <D2D1.h>

声明全局变量

首先我们需要一个ID2D1Factory*类型的对象,也就是D2D工厂接口,这个接口是所有D2D程序的起始点,几乎所有的D2D资源都是由这个接口创建的,其次我们需要一个渲染的场所,也就是Render Target,在D2D中有多种类型的Render Target,这里我们选择ID2D1HwndRenderTarget类型,用来在窗口中进行渲染。最后我们定义一个画刷,用来绘制图形,这里选择固定颜色的画刷,即ID2D1SolidColorBrush。

ID2D1Factory* pD2DFactory = NULL ; // Direct2D factory
ID2D1HwndRenderTarget* pRenderTarget = NULL;   // Render target
ID2D1SolidColorBrush* pBlackBrush = NULL ; // A black brush, reflect the line color RECT rc ; // Render area
HWND g_Hwnd ; // Window handle

创建D2D工厂

接下来创建D2D工厂对象,有了这个对象才能创建后续的资源,这个函数有两个参数,第一个参数是工厂的类型,这里只有单线程和多线程两类,如果是单线程的话,意味着D2D不会为所创建的工厂的对象以及由这个对象创建的子对象提供同步机制,也就是说,如果有多个线程访问了这个资源,那么需要自己提供同步机制。如果是多线程类型,那么D2D会为你提供同步机制。第二个参数用来接收创建的工厂。


HRESULT hr ;

hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &pD2DFactory) ;
if (FAILED(hr))
{
  MessageBox(hWnd, "Create D2D factory failed!", "Error", 0) ;
  return ;
}

创建Render target

有了工厂对象以后,开始创建RenderTarget,CreateHwndRenderTarget函数有三个参数,第一个参数是Render target属性,包括渲染模式,象素格式,DPI等,D2D提供了一个函数D2D1::RenderTargetProperties(),可以用来生成默认的属性,我们这里直接使用这个函数。第二个参数是Hwnd类型的Render target属性,它包含三个参数,第一个是窗口句柄,第二个是Render target的大小,第三个参数是Present选项,这个参数有个默认值,这里我们使用它的默认值。CreateHwndRenderTarget函数的最后一个参数用来接收创建的Render target。


// Create a Direct2D render target
hr = pD2DFactory->CreateHwndRenderTarget(
  D2D1::RenderTargetProperties(),
  D2D1::HwndRenderTargetProperties(
    hWnd, 
    D2D1::SizeU(rc.right - rc.left,rc.bottom - rc.top)
  ), 
  &pRenderTarget
) ;
if (FAILED(hr))
{
  MessageBox(hWnd, "Create render target failed!", "Error", 0) ;
  return ;
}

创建画刷

有了Render target,再使用函数CreateSolidColorBrush创建画刷,这里创建一个固定颜色的画刷,第一个参数是画刷的颜色,第二个参数接收创建的画刷,画刷的颜色就是绘制线条所用的颜色,比如这里创建一个红色的画刷,那么后面绘制的矩形就是红色的。


// Create a brush
hr = pRenderTarget->CreateSolidColorBrush(
  D2D1::ColorF(D2D1::ColorF::Red),
  &pBlackBrush
) ;
if (FAILED(hr))
{
  MessageBox(hWnd, "Create brush failed!", "Error", 0) ;
  return ;
}

绘制矩形

万事俱备,只欠渲染!渲染的代码很简单,首先调用CreateD2DResource函数来创建资源,这是个自定义函数,用来创建资源,比如Render target,画刷之类的,该函数包含了上面提到的代码。绘制的代码要放在BeginDraw和EndDraw函数之间,调用Clear函数可以将Render target清除为指定的背景色。DrawRectangle函数用来绘制矩形,它有两个参数,第一个是被绘制的矩形,第二个是绘制所用的画刷。函数EndDraw的返回值标识了渲染是否成功。


VOID DrawRectangle()
{
  CreateD2DResource(g_Hwnd) ;   pRenderTarget->BeginDraw() ;   // Clear background color white
  pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));   // Draw Rectangle
  pRenderTarget->DrawRectangle(
  D2D1::RectF(100.f, 100.f, 500.f, 500.f),
  pBlackBrush
); HRESULT hr = pRenderTarget->EndDraw() ; if (FAILED(hr))
{
  MessageBox(NULL, "Draw failed!", "Error", 0) ;
  return ;
}

清理资源

最后,在程序退出时,清理程序资源,每个COM对象都有一个Release方法,用来释放自己,这里我们定义一个宏来释放COM对象。

VOID Cleanup()
{
  SAFE_RELEASE(pRenderTarget) ;
  SAFE_RELEASE(pBlackBrush) ;
  SAFE_RELEASE(pD2DFactory) ;
}

释放COM对象的宏。

#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}

效果图如下

代码下载

Happy Coding

== THE END ==

Direct2D教程(二)来看D2D世界中的Hello,World的更多相关文章

  1. MVC教程二:从控制器中获取URL的值

    一.从控制器中获取URL的值有三种方式: 1.使用Request.QueryString[] 例如: string value = Request.QueryString["BookId&q ...

  2. Direct2D教程(外篇)环境配置

    2014年世界杯首场淘汰赛马上开始了,闲着没事,整理以前的博客草稿打发时间,意外的发现这篇文章,本来是打算加入到Direct2D那个系列的,不知道为什么把它给遗漏了.环境配置,对于熟手来说,不是什么重 ...

  3. Laravel教程 二:路由,视图,控制器工作流程

    Laravel教程 二:路由,视图,控制器工作流程 此文章为原创文章,未经同意,禁止转载. View Controller 上一篇教程我们走了那么长的路,终于把Laravel安装好了,这一篇教程我们就 ...

  4. SharpDX之Direct2D教程I——简单示例和Color(颜色)

    研究Direct2D已经有一段时间了,也写了一个系列的文章 Direct2D ,是基于Windows API Code Pack 1.1.在前文 Direct2D教程VIII——几何(Geometry ...

  5. Direct2D教程IV——笔刷(Brush)对象

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  6. Direct2D教程II——绘制基本图形和线型(StrokeStyle)的设置详解

    目前,在博客园上,相对写得比较好的两个关于Direct2D的教程系列,分别是万一的Direct2D系列和zdd的Direct2D系列.有兴趣的网友可以去看看.本系列也是介绍Direct2D的教程,是基 ...

  7. 无废话SharePoint入门教程二[SharePoint发展、工具及术语]

    一.前言 1.由于上一篇文章的标题命名失误,此篇标题写给百度搜索”什么是SharePoint”. 2.关于什么是SharePoint,请参见本人的第一篇文章:http://www.cnblogs.co ...

  8. SharpDX之Direct2D教程II——加载位图文件和保存位图文件

    本系列文章目录: SharpDX之Direct2D教程I——简单示例和Color(颜色) 绘制位图是绘制操作的不可缺少的一部分.在Direct2D中绘制位图,必须先利用WIC组件将位图加载到内存中,再 ...

  9. 【Visual C++】游戏开发五十六 浅墨DirectX教程二十三 打造游戏GUI界面(一)

    本系列文章由zhmxy555(毛星云)编写,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/16384009 作者:毛星云 ...

随机推荐

  1. XP系统连接win10家庭版共享的打印机方法

    1.高级共享设置.按照win7正常设置."家庭网络"公用网络”“工作网络”之类的注意根据当前配置设置! 2.由于控制面板无法开启Guest账户.需要用任务管理器,运行cmd(管理员 ...

  2. Apache简易快速安装

    转发出处:https://blog.csdn.net/qq_34804120/article/details/78862290 准备安装包 到https://www.apachelounge.com/ ...

  3. Java-创建一个线程

    第一种继承Thread类 package com.tj; public class BasicThread1 extends Thread { public void run() { System.o ...

  4. css中可继承和不可继承属性

    一.无继承性的属性 1.display:规定元素应该生成的框的类型 2.文本属性: vertical-align:垂直文本对齐 text-decoration:规定添加到文本的装饰 text-shad ...

  5. 2章 perl标量变量

    标量变量 单单存储一个值得变量   ,单个标量值 $name   为变量  区分大小写 $barney=$barney*2   第一次  取值  等号右边    :第二次  赋值 等号左边 双目操作符 ...

  6. mysql使用日常备忘

    批量插入数据时,如果主键是有业务意义的,并非自自增张,那么有可能在插入的数据中有与已存在的键值重复的,可以用如下方式来插入: INSERT IGNORE 当要插入一个数据时,插入的字段值中主键字段或唯 ...

  7. ruby操作mysql

    require "win32ole" require 'pathname' require 'mysql2' excel = WIN32OLE.new('excel.applica ...

  8. 【bzoj2111】[ZJOI2010]Perm 排列计数 dp+Lucas定理

    题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Mogic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Mogic的,答案可能很 ...

  9. 大杂烩 Classpath / Build path / Debug关联源码 / JDK&JRE区别

    Classpath的理解及其使用方式 原文地址:http://blog.csdn.net/wk1134314305/article/details/77940147?from=bdhd_site 摘要 ...

  10. 如何在github上寻找漏洞

    https://github.com/search?p=2&q=eval%28%24_POST[&ref=searchresults&type=Code   1.$_GET s ...