# 第一部分:Direct2D
Direct2D 是一个图形应用编程接口(Application Programming Interface),旨在渲染二维矢量和光栅图形。它建立在 Direct3D 应用编程接口之上,而 Direct3D 应用编程接口又建立在 DXGI (DirectX 图形基础设施)之上。它可以与 Direct3D 结合使用,渲染场景的任何二维部分。它具有高性能,利用图形处理器实现高效、复杂的二维图形。
| ![](img/note.png) | 注:在这本书里,我会多次提到图形处理器(图形处理单元的简称)。GPU 这个术语通常指的是一个专用的图形卡,然而,我将更一般地使用这个术语来指在计算机中执行大多数图形处理的硬件。这包括专用显卡、板载显卡或 WinRT 设备中 NVidia Tegra 芯片中的执行单元。 |
该应用编程接口由许多接口(组件对象)组成,用于与图形硬件通信。它可以渲染矢量图元,如直线和椭圆,还可以用纯色或渐变填充形状,以及显示光栅图像。光栅图形由像素组成,屏幕(或图像)上的每个点对应一个像素。每个像素都有决定其颜色的值,它们共同排列在一个大网格中。
Direct2D 对于可视化数据很重要,因为许多图表类型(折线图、散点图等)都是如此。)在设计上基本上是二维的。使用 Direct2D 和使用 Direct3D 渲染二维图形最重要的区别是简单。Direct3D 比 Direct2D 快几个数量级,但编程更复杂。除此之外,Direct2D 项目模板是标准 Windows 8 XAML 和 Direct2D 的完美结合。这允许程序员使用标准的 Windows 8 控件和 XAML 页面来处理用户输入,而 Direct2D 处理所有的图形处理。DirectX 和 XAML 的这种结合是仅在 Windows 8 应用程序中可用的功能。
![](img/image002.png)
图 1:主要 DirectX 组件之间的关系
图形驱动程序是描述的最低级别;它直接控制硬件。这上面是 DirectX 图形基础设施(DXGI),然后是 Direct3D,最后是 Direct2D。软件光栅化器用于代替图形硬件,当专用图形处理器不可用时,它使用中央处理器来渲染图形。
## 第 1 章:Direct2D (XAML)模板
我们将首先创建一个标准的 Direct2D (XAML)模板项目,并熟悉其结构。打开 Visual Studio 2012,在**文件菜单**上,点击**新建项目**。
![](img/image003.jpg)
图 2:创建新的 Direct2D 应用程序(XAML)
点击左侧面板的 **Visual C++** ,然后从中间面板的项目模板中选择 **Direct2D App (XAML)** 。在**名称**框中输入项目名称,然后点击**确定**。
Visual Studio 将为新项目创建许多文件,其中包含样板代码和一些其他有用的帮助方法。解决方案资源管理器应该如图 3 所示。
要在调试模式下运行应用程序,请按 F5,或在**文件**菜单上单击**调试** > **开始调试**。在 Visual Studio 生成并链接您的项目文件后,它将执行应用程序。
![](img/image004.jpg)
图 3: Direct2D 应用程序(XAML)解决方案资源管理器
![](img/image005.png)
图 Direct2D 应用程序(XAML)模板的输出
资产文件夹
该文件夹包含新应用程序的多个巴布亚新几内亚图像:
* Logo.png:此图像显示为 Windows 8 起始页上的图块。它类似于以前版本的 Windows 中的桌面图标。
* SmallLogo.png:这是应该显示较小图标时使用的图标图像,例如当用户在 Windows 8 中搜索“所有应用程序”时。
* SplashScreen.png:在执行应用程序时,闪屏会短暂出现。
* 这是你的应用出现在视窗商店的标志。
普通的
该文件夹包含一个 XAML 文件,描述了 XAML 文件之间的常见设置。
外部依赖关系:
此文件夹包含项目可能依赖的大量外部文件。有些是按项目生成的,有些是标准的 Windows C++头文件。您不应该更改此列表中的文件,尤其是标准的窗口标题。
应用
App.xaml、App.cpp 和 App.h 文件定义了您的应用程序。XAML 文件包含整个应用程序的一些全局设置。CPP 和 H 文件定义了一个类,该类具有执行程序的起始点。这个类拥有一个名为 m_directXPage 的成员变量,它是主要的 Direct2D 渲染类。它还控制一些重要的系统级操作,如在程序暂停时保存和恢复应用程序的状态。
巴斯蒂米尔
基本定时器头定义了一个类,可以用于任何基于时间的任务,如物理或动画。
xxx_TemporaryKey.pfx
这是您的应用程序的 ClickOnce 数字证书。它用于帮助确保应用程序不是恶意软件。如果应用程序没有签名,Windows 将警告用户该应用程序“来自未知的发行商”,并询问他们是否确定要运行该程序。
DirectXBase
DirectXBase 类定义在两个文件中:DirectXBase.h 和 DirectXBase.cpp。这个类包含了启动和运行 Direct2D 的大部分样板代码。它包含初始化设备、工厂、设备上下文和许多其他东西的代码。它可以用于二维和三维图形。它有许多助手功能,使我们能够快速开始 DirectX 编程,而无需键入极其冗长的样板代码。我们鼓励读者彻底调查这个文件,因为它确切地显示了 DirectX 应该如何初始化。
DirectXHelper
这个文件由一个函数组成,DX::ThrowIfFailed。这是一个将 HRESULT 转换为托管 C++异常的助手函数。DirectX 函数调用返回一个 HRESULT。我们将检查的许多代码都围绕着对这个方法的调用来调用 DirectX 函数,这样程序员就有机会检查 DirectX 抛出的任何错误。如果您在这一行设置了断点,Visual Studio 将在抛出异常时中断,并允许您检查出了什么问题。错误会给你一个错误号,你可以研究它的含义,或者使用 DirectX SDK 附带的错误搜索应用程序来查找它。
DirectXPage
这是你申请的 XAML 主页。Direct2D (XAML)模板应用程序包含一个简单的页面,其中两个句子写在 XAML 表单上。上面一句是用 XAML 写的,下面一句是用 DirectX 写的。这是呈现最上面一行代码的类。
Package.appxmanifest
这是您的应用程序的主要清单。它包含关于您的应用程序的所有信息,包括发行者是谁,以及应用程序需要什么功能(互联网访问、网络摄像头访问等)。).
预编译头文件
预编译头文件(pch.h)包含编译成中间格式的头文件,以便在重新编译整个项目时节省时间。为了正确工作,您添加到项目中的大多数类都将包含此文件。
简单文本渲染器
这是这个 DirectX 应用程序的核心类。这个类在屏幕上呈现下面的句子。因为 SimpleTextRenderer 类是控制 DirectX 在屏幕上显示什么的主要类,所以我们将详细研究它。
### 简单文本渲染器类
这个类使用 Direct2D 向屏幕呈现一行文本。在这一节中,我们要研究的不是类本身,而是它的运行方式。我们将在未来章节中构建的图形渲染器类将主要基于这个类。打开 SimpleTextRenderer.h 文件。
该类从 DirectXBase 类派生。它包含一个默认构造函数和几个方法,在资源分配过程中调用(`CreateDeviceIndependentResources`、`CreateDeviceResources`和`CreateWindowSizeDependentResources`)。
| ![](img/note.png) | 注意:Resources 是一个通用术语,指的是存储在内存(系统内存或图形处理器的专用内存)中并由 DirectX 使用的许多不同类型的对象和结构。资源在使用之前必须被创建和初始化。我们将研究的大多数资源都是在主 DirectX 对象之后不久创建的。当应用程序关闭时,这些资源会被销毁。资源可以在初始化主 DirectX 对象后的任何时候创建和销毁,因为资源创建方法属于这些对象。 |
渲染方法是 DirectX 进行所有渲染的地方。此类还定义了一个更新方法,可用于执行计算,以确定对象应该移动到场景中的什么位置。`UpdateTextPosition`、`BackgroundNextColor`、`BackgroundPreviousColor`功能是这个模板特有的,自己开发的时候不需要。它们允许用户操纵 DirectX 绘制的文本的位置,以及在一些预定义的背景颜色之间循环。
使用`SaveInternalState`和`LoadInternalState`方法保存和恢复应用程序的状态;例如,当 WinRT 平板电脑进入睡眠状态,然后被唤醒。
这些方法后面有几个成员变量,用于维护和操作文本的位置。除了`m_renderNeeded`变量之外,这些变量大多数都是特定于应用程序的,并且很可能不是您的应用程序所必需的。应用程序使用`m_renderNeeded`变量来确定是否应该调用渲染方法。如果场景中没有任何变化,那么再次渲染就没有意义了。下图描述了这个应用程序中最重要的类之间的关系。以菱形结束的线条表示所有权(AppXAML 类拥有 DirectXPage 类型的成员),以三角形结束的线条表示继承(SimpleTextRenderer 从 DirectXBase 类继承)。
![](img/image006.png)
图 Direct2D 应用程序(XAML)模板的类关系
实时图形应用程序通常以某个预定义的时间间隔渲染帧,该时间间隔用每秒显示的帧数(FPS)来描述。帧是游戏或电影的单个静止图像。为了创建平滑动画的错觉,稍微不同的帧被连续显示给观众。DirectX 应用程序通常以固定的刷新率渲染帧,例如 60fps 甚至 100fps。不太可能每隔 60 秒或 100 秒重新渲染一次制图应用程序的帧。它们通常长时间保持完全相同。用户可以平移或缩放图表,这将需要场景的重新渲染,但是这个动作不像更新实时游戏的帧那样对时间很关键。
| ![](img/note.png) | 注意:这个模板中这个类和其他类的成员变量有一个“m_”前缀。这意味着它们是成员变量,而不是函数的局部变量。没有必要,但是用这个前缀命名所有成员变量是个好主意。 |
接下来,在解决方案资源管理器中打开 SimpleTextRenderer.cpp 文件。在文件的顶部,您将看到预编译头(`pch.h`)的`#include`指令。下面是`SimpleTextRenderer.h,`的`#include`和该类使用的名称空间列表。在使用指令下,您将看到预定义的背景颜色顺序,用户可以在运行应用程序时循环使用这些颜色。
```cpp
static const ColorF BackgroundColors[] = { … }
```
用户可以通过在屏幕上右键单击鼠标,或者在使用触摸屏设备时滑动指针,并选择**下一步**或**上一步**来循环切换颜色和更改应用程序的背景。这是一个特定于应用程序的数组,其他应用程序不太可能使用它。在下面,我们看到了类的默认构造函数。
```cpp
SimpleTextRenderer::SimpleTextRenderer():m_renderNeeded(true), m_backgroundColorIndex(0),m_textPosition(0.0f, 0.0f) { }
```
默认构造函数初始化几个变量;它通过选择索引 0 将`backcolor`设置为`CornflowerBlue`(这是对前面代码示例中定义的`BackgroundColors`数组的引用)。它还初始化文本位置,并将 m_ `renderNeeded`布尔值设置为真,这样第一帧将被绘制到屏幕上。此时不会创建或分配资源;DirectX 工厂和上下文也不存在。
接下来是三种资源分配方法。首先是`CreateDeviceIndependentResources`法。
```cpp
void SimpleTextRenderer::CreateDeviceIndependentResources() {
DirectXBase::CreateDeviceIndependentResources();
DX::ThrowIfFailed(
m_dwriteFactory->CreateTextFormat(
L"Segoe UI", nullptr, DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL, DWRITE_FONT_STRETCH_NORMAL,
42.0f, L"en-US", &m_textFormat));
DX::ThrowIfFailed(
m_textFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING)
);
}
```
`CreateDeviceIndependentResources`方法用于创建和初始化任何独立于设备的 Direct2D 对象。这个方法从调用基类的同名方法开始。基类方法创建 DirectX 工厂,比如下一行使用的`m_dwriteFactory`,应用程序可以使用它来创建更多的 DirectX 对象。
| ![](img/note.png) | 注意:DirectX 中的资源都来自两大类之一:设备资源或与设备无关的资源。设备是显卡,设备资源驻留在显卡本身。独立于设备的资源驻留在系统内存中,渲染速度往往较慢,因为它们需要 CPU 周期来传输到显卡。 |
```cpp
void SimpleTextRenderer::CreateDeviceResources() {
DirectXBase::CreateDeviceResources();
DX::ThrowIfFailed(
m_d2dContext->CreateSolidColorBrush(
ColorF(ColorF::Black), &m_blackBrush));
Platform::String^ text = "Hello, DirectX!";
DX::ThrowIfFailed(m_dwriteFactory->CreateTextLayout(
text->Data(), text->Length(),
m_textFormat.Get(),
700, // maxWidth.
1000, // maxHeight.
&m_textLayout));
DX::ThrowIfFailed(m_textLayout->GetMetrics(&m_textMetrics));
}
```
`CreateDeviceResources`方法创建并初始化设备相关资源。该方法调用同名的基类方法,该方法创建 Direct3D 设备的实例和应用程序使用的上下文(`m_d3dcontext`和`m_d3dDevice`)。
| ![](img/note.png) | 注意:设备和上下文是 DirectX 中的两个重要术语。设备可以看作是显卡本身;这个类用于初始化硬件,查询其功能,并创建资源,如纹理和着色器。上下文是设备的特定用途;它使用设备上的资源将内容呈现到屏幕上。通常有一个设备,但可能有多个上下文。例如,打印示例使用三个上下文:一个用于渲染,另一个用于打印预览,第三个用于打印本身。图 6 显示了这些类各自负责的一些任务。 |
![](img/image009.png)
图 6:设备与环境
画笔是设备资源;此方法创建一个黑色画笔来绘制文本。要写入屏幕的实际字符串在设备上使用`CreateTextLayout`方法创建为`TextLayout`对象。之后,使用`GetMetrics`方法将弦的尺寸和比例保存到`m_textMetrics`中。
| ![](img/note.png) | 注意:CreateTextLayout 方法创建 IDWriteTextLayout 设备资源。此资源包含有关要打印的字符串、打印该字符串的边界框及其位置的信息。CreateTextFormat 方法(在 CreateDeviceIndependentResources 方法中)创建一个 IDWriteTextFormat 对象,该对象用于指定要呈现的文本的字体、大小和属性。 |
```cpp
void SimpleTextRenderer::CreateWindowSizeDependentResources() {
DirectXBase::CreateWindowSizeDependentResources();
// Add code to create window size dependent objects here.
}
void SimpleTextRenderer::Update(float timeTotal, float timeDelta) {
(void) timeTotal; // Unused parameter.
(void) timeDelta; // Unused parameter.
// Add code to update time dependent objects here.
}
```
前面两个方法在模板中是空的。`CreateWindowSizeDependentResources`方法用于创建其设置取决于屏幕大小或方向的任何对象(设备或独立设备)。`Update`法也是空的;它控制应用程序的物理或其他逻辑,通常是依赖于时间的东西。以下代码是模板的`Render`方法的示例。
```cpp
void SimpleTextRenderer::Render() {
m_d2dContext->BeginDraw();
m_d2dContext->Clear(ColorF(BackgroundColors[m_backgroundColorIndex]));
// Position the rendered text.
Matrix3x2F translation = Matrix3x2F::Translation(
m_windowBounds.Width / 2.0f –
m_textMetrics.widthIncludingTrailingWhitespace / 2.0f +
m_textPosition.X,
m_windowBounds.Height / 2.0f –
m_textMetrics.height / 2.0f + m_textPosition.Y
);
// Note that the m_orientationTransform2D matrix is post-multiplied here
// in order to correctly orient the text to match the display orientation.
// This post-multiplication step is required for any draw calls that are
// made to the swap chain's target bitmap. For draw calls to other targets,
// this transform should not be applied.
m_d2dContext->SetTransform(translation * m_orientationTransform2D);
m_d2dContext->DrawTextLayout(Point2F(0.0f, 0.0f),
m_textLayout.Get(), m_blackBrush.Get(),
D2D1_DRAW_TEXT_OPTIONS_NO_SNAP);
// Ignore D2DERR_RECREATE_TARGET. This error indicates that the device
// is lost. It will be handled during the next call to Present.
HRESULT hr = m_d2dContext->EndDraw();
if (hr != D2DERR_RECREATE_TARGET) {
DX::ThrowIfFailed(hr);
}
m_renderNeeded = false;
}
```
场景的实际绘制就是在渲染方法中进行的。场景的大部分绘制都是由 m_d2dContext 对象完成的。渲染方法从陈述`m_d2dcontext->BeginDraw`开始;这条线耦合到底部附近的`m_d2dContext->EndDraw`方法调用。您应该将所有的 Direct2D 绘图放在这两个函数调用之间。BeginDraw 用于指定一些代码的开始,这些代码为渲染目标构建一批渲染命令。EndDraw 指定该批命令已经完成,可以进行渲染。
下一行调用`Clear`方法,传递用户当前选择的颜色。这导致屏幕被清除为纯色,这是先前定义的`BackgroundColors`数组,用户可以在其中循环。
| ![](img/tip.png) | 提示:在渲染方法中将屏幕清除为黑色以外的颜色是一个好主意,即使您的后续绘图将完全覆盖清除的屏幕。如果您不这样做,并且程序有问题,您可能会盯着黑屏(或随机闪烁的颜色或像素),根本无法知道渲染方法是否正在被调用。 |
在调用`Clear`之后,建立一个矩阵。诸如缩放、旋转和平移(或平移,这就是我们在这里所做的)等变换都由矩阵控制。这个特定的矩阵是一个翻译矩阵;它移动文本,以便用户可以在屏幕上拖动它。这个矩阵定义中的计算将文本放在屏幕中间,当用户拖动它时会有一些偏移。它使用`TextMetrics`对象和`WindowBounds`对象来找到文本应该去的地方。
一旦定义,翻译矩阵必须应用于上下文。这发生在调用`SetTransform`的下一行。在应用了适当的转换之后,可以呈现文本本身。这发生在呼叫`DrawTextLayout`的下一条线路上。然后调用`EndDraw`结束绘制,图像呈现给用户。
| ![](img/tip.png) | 提示:当 m_renderer 对象在其 OnRendering 事件处理程序方法中调用 Present()时,渲染场景的实际屏幕刷新发生在 DirectXPage.xaml.cpp 文件中。非常重要的一点是,DirectXPage 类呈现了场景。当您添加调用 Present()本身的更复杂的呈现类时,从 DirectXPage 类中移除 Present()调用是很重要的。否则,您可能会出现()两次,这将导致首先将实际场景翻转到屏幕上,但立即用其他图像覆盖它。 |
剩下的方法是事件处理程序和其他特定于此的东西。我建议刚刚使用 Visual Studio 2012 熟悉 DirectX 的程序员在继续下一节之前花一些时间来改变这个模板的工作方式。熟悉这个模板对于理解本书 Direct2D 部分的其余章节至关重要。
| ![](img/tip.png) | 提示:Direct2D 设计用于在渲染几何图形时自动使用 CPU 的多个内核。如果在 DirectXBase.cpp 文件中创建设备上下文时使用 d2d 1 _ DEVICe _ CONTENT _ OPTIONS _ ENable _ 多线程 _OPTIMIZATIONS 选项,自动多线程可能会大大提高代码的速度,但代价是占用更多的系统内核。 |
### 同步、交换链和缓冲
电脑显示器以固定的速度更新显示。每秒 60 次是常见的,称为 60 Hz,但也有其他类似的 75 Hz 和 100 Hz。像素数据存储在图形处理器的缓冲区中,称为前缓冲区。监视器上的图像每秒钟被来自该缓冲区的数据刷新 60 次。在显示器刷新显示的同时,图形处理器正忙于渲染要显示的帧。图形处理器将像素数据写入缓冲区。
这个系统有一个问题,导致了令人不快的伪像。问题是 GPU 和显示器不一定以相同的速度更新帧。这导致了一种叫做撕裂的假象(见图 7)。显示器将一帧的一半绘制到屏幕上,将前一帧的一半绘制到屏幕上,因为当显示器部分通过更新其显示时,GPU 会更新前缓冲区中的帧。
![](img/image012.png)
图 7:撕裂
为了避免这种情况,GPU 不会渲染到前缓冲区。相反,它呈现到后缓冲区,除了没有呈现到屏幕上之外,它在各个方面都与前缓冲区相同。
监视器通过将屏幕左上角的像素渲染到右下角来刷新显示,然后重置并重复该操作。它从右下角重置回顶部的时间段称为垂直回扫。为了避免撕裂伪影,图形处理器等待监视器处于垂直回扫阶段,然后翻转缓冲器(通过复制像素数据或交换指针来交换前后缓冲器)。这被简称为垂直同步或垂直同步。当显示器完成自我重置时,图形处理器可以将整个帧复制到前缓冲区。这样就不会撕裂,显示器永远不会显示一帧的一半和另一帧的一半。
使用交换链对象协调缓冲区。这是一个专用于控制缓冲区交换的类。在我们的应用中,有两个缓冲区:前缓冲区和后缓冲区。有时,使用多个后台缓冲区并逐个渲染帧,对要显示的帧进行排队是有益的。
## 第 2 章:使用 WinRT 设备进行调试
本书中的所有代码都适用于 Windows 8 电脑以及 WinRT 设备。如果您正在为 WinRT 平板电脑创作软件,并且有一个真实的设备,那么使用它来调试和测试您的应用程序而不是仿真器(通常是默认的)是非常有益的。C++和 DirectX 中的大部分代码在 Windows 8 PC 以及 WinRT 设备(为 ARM 目标编译)上运行良好。模拟器很好,但永远无法与真实设备的确切特征相匹配。
安装远程工具
将 Visual Studio 2012 的远程工具安装到设备上。这可从微软网站上获得(可从[http://www . Microsoft . com/visualstudio/eng/downloads # d-additional-software](http://www.microsoft.com/visualstudio/eng/downloads#d-additional-software)获得)。它是一种与 Visual Studio 开发机器连接的服务,用于在设备上运行和调试应用程序。Visual Studio 提供了所有常规调试机制,如断点、检查 ARM 寄存器和内存窗口。您需要知道设备的名称,以便在其上部署应用程序。您还需要让设备运行前面提到的安装附带的删除调试监视器。每个构建配置(发布 x86、调试 x86、发布 ARM 等。)您希望设备运行的项目设置中必须有该设备的名称。
将应用程序更改为 ARM
如果您要部署到的 WinRT 设备是基于 ARM 的,例如微软 Surface,您可以通过选择 **ARM** 从主菜单中更改项目的配置。
![](img/image013.png)
图 8:配置
将调试更改为远程计算机
如果尚未设置,您应该将调试更改为远程计算机。
![](img/image014.jpg)
图 9:机器名称
指定远程计算机的名称
从 Visual Studio 的主菜单中打开**项目** > **【名称】属性**页面,或者在解决方案资源管理器中右键单击项目名称,然后在上下文菜单中单击**属性**。这将打开项目的属性页。点击左侧面板的**调试**,在标有**机器名称**的空白处输入您的远程机器名称。
![](img/image015.png)
图 10:远程机器
运行远程调试器
在设备上运行远程调试服务,您应该可以像往常一样从 Visual Studio 2012 开始调试(按 F5 或单击开始调试按钮)。您将在设备(Visual Studio 远程调试器的窗口)上看到的第一件事是,它已连接到开发计算机,并显示如下消息:
`3/01/2013 2:48:40 PM [MachineName]\[ComputerName] connected`
此后不久,您将在 Visual Studio 的输出窗口中看到一条消息,称它正在将程序上传到设备。这需要一些时间,但是一旦上传完成,应用程序应该会运行。
如果您无法从设备调试应用程序,或者应用程序没有按预期运行,以下是一些想法:
* 确保设备上安装了正确的远程调试工具。安装 Visual Studio 2012 的工具。请务必直接从 Microsoft 网站下载此文件,并下载任何可用的更新,以确保当前的远程调试支持您的特定设备。
* 请确保在项目属性中正确拼写了远程计算机的名称。远程计算机名是在首次安装 Windows RT 时选择的。如果您忘记或不确定远程计算机的名称,可以在“远程调试器”窗口中看到远程计算机的名称。目前,项目属性中名称的大小写无关紧要,但是机器使用所有大写字母,因此您可能会尝试匹配机器使用的确切大小写。
* 确保当前配置具有在属性页的调试字段中指定的远程计算机的名称。您需要在每个配置中输入设备的名称。例如,如果您使用调试和发布,您需要在两者中指定远程机器的名称。
最后,如果应用程序没有按预期执行而是正在运行,请确保您使用的代码完全可移植到 WinRT。请注意,这些设备没有专用显卡。它们依赖于缩小规模、便携且节能的中央处理器/图形处理器组合。在这些设备上运行的 DirectX 11 版本也在缩小。它不包含 DirectX 11 标准的全部功能。操作系统本身(Windows RT)是完整 Windows 8 的缩小版本,许多功能缺失(例如,自由访问文件结构)。
## 第 3 章:开始图形渲染应用
图 11 是一个基本的条形图。这个特殊的是使用开放办公室计算与随机数据生成的。它由标题、背景、轴标签、网格、键和代表数据的条组成。
![](img/image016.jpg)
图 11:条形图
图表的每个部分都可以被认为是一个独特的对象。每个对象一个接一个地呈现,从背景开始,然后是网格、数据,然后是标签。图形本身由几个对象组成,它一个接一个地绘制这些对象,以构建数据的完整图形表示。上一张图表的许多内容是通用的,适用于不同的图表类型。例如,网格可以用于散点图、折线图或直方图,就像这里使用的一样。
我们的制图应用程序将以同样的方式工作。我们将开发一个图表对象的集合,可以随意在图表中添加和删除。这些对象将是非常基本的,以维护一个通用的和可用的 Direct2D 图表应用程序的基础。图形本身将是一个名为 GraphRenderer 的类,它将基于我们刚刚检查的 SimpleTextRenderer 类。构成图形渲染器的每个对象都是简单文本渲染器的缩小版本。
在 Visual Studio 2012 中为 Windows 8 创建新的 Direct2D (XAML)应用程序。这将成为我们应用的起点。我已经调用了我的应用程序 graph 标绘。如果复制代码进行测试,您需要将对此命名空间的任何引用更改为应用程序的名称。
首先,我们可以删除表单上的 XAML 文本。通过在解决方案资源管理器中双击其名称,打开 DirectXPage.xaml 文件。这将在可视化设计器中显示页面。选择**你好,XAML!**并右键单击。点击上下文菜单中的**删除**。
![](img/image017.jpg)
图 12:删除文本
在这个面板的下侧还有一个隐藏栏,可以删除。它是当用户在屏幕上右键单击时出现的条,允许背景颜色改变。该栏在设计器中不可见,因此从 XAML 代码中删除它是最容易的。我已经突出显示了要删除的行。
```cpp
… Lots of XAML code here…!!!
```
打开 **DirectXPage.xaml.cpp** 文件。删除`OnPreviousColorPressed`和`OnNextColorPressed`事件处理程序。在这个类的构造函数中,创建了一个 SimpleTextRenderer 对象。我们需要将它改为一个 GraphRenderer 构造函数调用。也可以删除状态保存方式,`SaveInternalState`和`LoadInternalState`。GraphRenderer 类使用了一个名为`PointerMoved`的方法,而不是`UpdateTextPosition`。该方法在代码列表中已被重命名。整个 DirectXPage.xaml.cpp 文件应该如下所示:
```cpp
// DirectXPage.xaml.cpp
#include "pch.h"
#include "DirectXPage.xaml.h"
using namespace GraphPlotting;
using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::Graphics::Display;
using namespace Windows::UI::Input;
using namespace Windows::UI::Core;
using namespace Windows::UI::Xaml;
using namespace Windows::UI::Xaml::Controls;
using namespace Windows::UI::Xaml::Controls::Primitives;
using namespace Windows::UI::Xaml::Data;
using namespace Windows::UI::Xaml::Input;
using namespace Windows::UI::Xaml::Media;
using namespace Windows::UI::Xaml::Navigation;
DirectXPage::DirectXPage() : m_renderNeeded(true), m_lastPointValid(false) {
InitializeComponent();
m_renderer = ref new GraphRenderer();
m_renderer->Initialize(Window::Current->CoreWindow, SwapChainPanel,
DisplayProperties::LogicalDpi);
Window::Current->CoreWindow->SizeChanged +=
ref new TypedEventHandler(this,
&DirectXPage::OnWindowSizeChanged);
DisplayProperties::LogicalDpiChanged +=
ref new DisplayPropertiesEventHandler(this,
&DirectXPage::OnLogicalDpiChanged);
DisplayProperties::OrientationChanged +=
ref new DisplayPropertiesEventHandler(this,
&DirectXPage::OnOrientationChanged);
DisplayProperties::DisplayContentsInvalidated +=
ref new DisplayPropertiesEventHandler(this,
&DirectXPage::OnDisplayContentsInvalidated);
m_eventToken = CompositionTarget::Rendering::add(ref new
EventHandler