使用 Direct2D 绘图
在 6 月月刊的 MSDN Magazine (msdn.microsoft.com/en-us/magazine/dd861344.asp x),我引入 Direct2D,一个全新的二维图形 API 旨在支持要求最高和直观地丰富桌面应用程序最可能的性能。 该本文我将介绍其中 Direct2D 适合在各种图形 Windows,其体系结构和原则的 API 之间。 特别,我详细介绍使用 Direct2D 可靠,并有效地呈现一个窗口内的基本知识。 这包括创建特定于设备的资源,以及独立于设备的资源和其各自的周期。 如果您没有这么做,我要鼓励您为这篇文章非常基于基础上此处,继续签出有布局之前应阅读该文章。
呈现和控件
最好考虑 Direct2D 为硬件加速的二维呈现 API。 当然,它支持后备,软件,但在这里是 Direct2D 呈现有关。 与其他图形在 Windows API 不同 Direct2D 一种组件化的方法所需的图形。 它不提供它自己的 API 编码和解码位图、 文本布局、 字体管理、 动画、 三维,依此类推。 而不是,它侧重于呈现,并同时提供第一次舱,图形处理单元 (GPU) 控制挂接到其他 API 等文本布局和图像的重点。 但是,Direct2D 会提供基元表示不同类型的画笔以及简单和复杂的形状时,构建基块的任何二维图形应用程序。
本文,我将介绍如何使用 Direct2D 绘制。 我将通过引入 Direct2D 的颜色结构开始,然后向您展示如何创建不同类型的画笔。 与大多数其他图形在 Windows API,不同 Direct2D 不提供"笔"基元的因此画笔是非常重要,因为它们用于大纲和填充的所有任务。 与开,我将介绍如何绘制基元的形状。
Colors
Direct2D 使用一个简单的结构,表示浮点颜色分量的颜色。 D2D1_COLOR_F 类型为实际 typedef D3DCOLORVALUE 结构使用 Direct3D 来描述颜色值。 它包括红色、 绿色、 蓝色和 alpha 通道的单个浮点的值。 值范围从 0.0 为到 1.0,0.0 是黑色的颜色通道和用于表示 alpha 通道完全透明的。
其外观如下:
struct D3DCOLORVALUE
{
FLOAT r;
FLOAT g;
FLOAT b;
FLOAT a;
};
Direct2D 提供 ColorF 帮助器类的继承 D2D1_COLOR_F 和定义了一些常见的颜色常数,但更重要的是提供了几个有用的构造函数初始化 D2D1_COLOR_F 结构 D2D1 命名空间中。 您可以渚嬪的方式 定义红色如下所示:
const D2D1_COLOR_F red = D2D1::ColorF(1.0f, 0.0f, 0.0f);
另一个构造函数接受一个打包的 RGB 值,并将其转换为单个颜色通道中。 下面的红色再次:
D2D1::ColorF(0xFF0000)
这是完全相同使用下面的枚举值:
D2D1::ColorF(D2D1::ColorF::Red)
尽管简洁,使用打包 RGB 表示不会吃了几个更多 CPU 周期为不同的颜色通道需要提取并转换为它们的浮动点等效的因此请谨慎使用。
所有的构造函数接受一个可选 alpha 值,该值默认为 1.0,或完全不透明。 因此,可以表示半透明的蓝色,如下所示:
D2D1::ColorF(0.0f, 0.0f, 1.0f, 0.5f)
除了清除呈现目标的绘图区域,但是,没有您可以使用一种颜色直接执行小。 您需要将画笔。
画笔
与简单的颜色的结构不同的画笔是通过接口公开的资源。 它们用于绘制线条、 绘制和填充形状,以及绘制文本。 从 ID2D1Brush 派生的接口表示不同类型的画笔 Direct2D 提供。 ID2D1Brush 接口本身允许您控制不透明度的画笔作为一个整体,而不是更改在使用该画笔的颜色的 alpha 通道。 这可能是更有趣的画笔类型中的一些特别有用。
下面是如何可能设置画笔的不透明度为 50%:
CComPtr<ID2D1Brush> brush;
// Create brush here...
brush->SetOpacity(0.5f);
ID2D1Brush 还允许您控制应用于画笔,因为与不同 Windows Presentation Foundation (WPF) 转换,画笔采用而不需要任何特定的形状,它们可能绘图的坐标系统的呈现目标。
创建所有的画笔的各种呈现目标方法接受的可选 D2D1_BRUSH_PROPERTIES 结构可用于设置初始的不透明度和转换。
所有 Direct2D 由提供该画笔是可变的并且可以有效地更改它们的特征,以便您不需要创建具有不同的特征的新的画笔。 记住这在设计应用程序。 画笔也是设备相关的资源,因此它们由创建它们的呈现目标的生存期绑定。 换而言,只要其呈现目标有效,但您必须释放呈现目标时释放它可以重复使用一个特定的画笔。
如其名称建议 ID2D1SolidColorBrush 接口表示纯色画笔。 它将添加方法,用于控制使用该画笔的颜色。 与呈现目标的 CreateSolidColorBrush 方法创建纯色画笔:
CComPtr<ID2D1RenderTarget> m_target;
// Create render target here...
const D2D1_COLOR_F color = D2D1::ColorF(D2D1::ColorF::Red);
CComPtr<ID2D1SolidColorBrush> m_brush;
HR(m_target->CreateSolidColorBrush(color, &m_brush));
This brush's initial color can also easily be changed using the SetColor method:
m_brush->SetColor(differentColor);
我要保留的形状下一部分的完整讨论,但的具有要查看的东西,我只需将填充窗口的呈现目标使用一个基于呈现目标的大小的矩形。 请记住 Direct2D 使用设备无关的像素 (DIPs)。 鍥犳 报告等不是一个桌面窗口工作区,特定设备的大小可能与不匹配呈现目标的大小。 幸运的是,很容易非常 DIPs 使用 GetSize 方法获取呈现目标的大小。 一个 D2D1_SIZE_F 结构与两个浮点表示大小的 Direct2D 使用 GetSize 返回点命名宽度和高度值。 然后,我可以提供描述区域以填充使用的 RectF 帮助器函数,并在报告的呈现目标大小插入 D2D1_RECT_F 变量。 最后,我使用呈现目标的 FillRectangle 方法可以执行实际的绘图。
如下代码如下所示:
const D2D1_SIZE_F size = m_target->GetSize();
const D2D1_RECT_F rect = D2D1::RectF(0, 0, size.width, size.height);
m_target->FillRectangle(rect, m_brush);
图 1 显示窗口中用绿色实心画笔的外观。 令人非常兴奋确实!
Direct2D 还提供了两种类型的渐变画笔。 渐变画笔是用沿某个轴混合的颜色填充的区域。 线性渐变画笔定义为一条直线具有开始和结束点的轴。 径向渐变画笔定义的椭圆从椭圆的相对于中心的某些点颜色的向外 radiate 坐标轴。
渐变被指一系列的相对位置从 0.0 到 1.0。 每个具有其自己的颜色,并被称为梯度停止点。 就可以使用这个范围之外的位置生成不同的效果。 若要创建渐变画笔,您必须首先创建渐变停止点集合。 首先,定义 D2D1_GRADIENT_STOP 结构的数组。
示例如下:
const D2D1_GRADIENT_STOP gradientStops[] =
{
{ 0.0f, color1 },
{ 0.2f, color2 },
{ 0.3f, color3 },
{ 1.0f, color4 }
};
下一步中,调用呈现目标的 CreateGradientStopCollection 方法创建集合对象基于的渐变停止点,数组,如下所示:
CComPtr<ID2D1GradientStopCollection> gradientStopsCollection;
HR(m_target->CreateGradientStopCollection(gradientStops,
_countof(gradientStops),
&gradientStopsCollection));
第一个参数是一个指向数组的并在第二个参数提供数组的大小。 此处我只需使用标准 _countof 宏。 CreateGradientStopCollection 方法返回新集合。 若要创建一个线性渐变画笔,还需要提供一个 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES 结构,以指示开始和结束点的渐变的轴。 与此渐变停止点位置不同这些点的是在通常是呈现目标的除非转换画笔上设置画笔的坐标空间中。 渚嬪的方式 可从运行的呈现目标在左上角到右下角,如下所示的坐标轴与创建画笔:
const D2D1_SIZE_F size = m_target->GetSize();
const D2D1_POINT_2F start = D2D1::Point2F(0.0f, 0.0f);
const D2D1_POINT_2F end = D2D1::Point2F(size.width, size.height);
const D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES properties = D2D1::LinearGradientBrushProperties(start, end);
HR(m_target->CreateLinearGradientBrush(properties,
gradientStopsCollection,
&m_brush));
LinearGradientBrushProperties Direct2D 初始化 D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES 结构由提供的另一个帮助器函数。 CreateLinearGradientBrush 方法接受此以及与上文所示渐变停止点集合,并返回表示新的画笔的 ID2D1LinearGradientBrush 接口指针。
当然,该画笔不会知道是否呈现目标的大小发生变化。 如果您希望将窗口调整大小时轴的结束点,轻松地操作,使用画笔的 SetEndPoint 方法。 您可以在同样,更改坐标轴的SetStartPoint 方法开始点。
如下绘图代码如下所示:
const D2D1_SIZE_F size = m_target->GetSize();
const D2D1_RECT_F rect = D2D1::RectF(0, 0, size.width, size.height);
m_brush->SetEndPoint(D2D1::Point2F(size.width, size.height));
m_target->FillRectangle(rect, m_brush);
图 2 显示窗口中用线性渐变画笔的外观。
若要创建一个径向渐变画笔,您需要提供 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES 结构。 此结构定义椭圆,以及一个偏移量相对于原点出的画笔"辐射"表示椭圆的中心渐变停止点集合所基于的颜色。 在下面的示例产生椭圆,在呈现目标中居中的 X 和 Y 的半径为与该呈现目标和一半朝着右下角的原点的:
const D2D1_SIZE_F size = m_target->GetSize();
const D2D1_POINT_2F center = D2D1::Point2F(size.width / 2.0f, size.height / 2.0f);
const D2D1_POINT_2F offset = D2D1::Point2F(size.width * 0.25f, size.height * 0.25f);
const float radiusX = size.width / 2.0f;
const float radiusY = size.height / 2.0f;
const D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES properties = D2D1::RadialGradientBrushProperties(center,
offset,
radiusX,
radiusY);
HR(m_target->CreateRadialGradientBrush(properties,
gradientStopsCollection,
&m_brush));
RadialGradientBrushProperties Direct2D 初始化 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES 结构由提供的另一个帮助器函数。 CreateRadialGradientBrush 方法接受此以及与上文所示渐变停止点集合,并返回表示新的画笔的 ID2D1RadialGradientBrush 接口指针。
还可以更改画笔的椭圆和原点在任何时候,如下所示:
m_brush->SetCenter(center);
m_brush->SetGradientOriginOffset(offset);
m_brush->SetRadiusX(radiusX);
m_brush->SetRadiusY(radiusY);
图 3 显示操作中径向渐变画笔。
Direct2D 还提供了一个位图画笔,但我将保持为将来项目的 Direct2D 位图的讨论。
Shape
到目前为止我已显示可以只如何填充一个矩形,以便我可以重点画笔,但 Direct2D 如此超过普通的矩形。 对于初学者的方式来说基元提供了矩形、 圆角的矩形和椭圆。 由基元,我只是意味着是为每个这些旧的纯数据结构。
D2D1_RECT_F 表示浮点矩形,本身的 D2D_RECT_F typedef:
struct D2D_RECT_F
{
FLOAT left;
FLOAT top;
FLOAT right;
FLOAT bottom;
};
D2D1_ROUNDED_RECT 表示圆角的矩形,并与定义将用于绘制角季度省略号,radiuses 按下面的方式这样定义:
struct D2D1_ROUNDED_RECT
{
D2D1_RECT_F rect;
FLOAT radiusX;
FLOAT radiusY;
};
D2D1_ELLIPSE 代表一个椭圆,并定义与一个中心点,以及 radiuses:
struct D2D1_ELLIPSE
{
D2D1_POINT_2F point;
FLOAT radiusX;
FLOAT radiusY;
};
尽管这些结构不可能看起来太令人兴奋,有最多前面我提到它们两个原因。 第一次,它们用于创建更复杂的几何图形对象。 第二个,如果您需要的所有填充或绘制这些基元之一,您应停留与它们为您通常将得到更好的性能,如果避免几何图形对象。
呈现目标提供一组的填充和绘图的方法填充,这些基元的大纲显示。 到目前为止我已介绍如何使用 FillRectangle 方法。 我不会钻孔您使用示例,该 FillRoundedRectangle 和 FillEllipse 方法完全相同的方式工作。 以填充方法相比,绘图的方法可用于一个特定形状轮廓而且很多功能。 与填充方法一样绘图方法覆盖所有三个的基元以完全在相同方式工作,因此我将介绍仅 DrawRectangle 方法。
最简单的形式是可以绘制,如下所示的一个矩形轮廓:
m_target->DrawRectangle(rect,
m_brush,
20.0f);
第三个参数指定要分级显示矩形绘制笔划的宽度。 因此如果您有同一矩形会看到填充和笔划 10.0 DIPs 通过重叠笔划本身所的矩形上居中。 一个可选的第四个参数可能提供来控制绘制笔划的样式。 这非常有用如果您需要绘制虚线或只是想控制在形状的顶点的联接的类型。
粗线的样式信息由 ID2D1StrokeStyle 接口表示。 笔划样式对象是这意味着他们不必重新创建呈现目标无效时的独立于设备的资源对象。 该事件中使用 Direct2D 工厂对象的 CreateStrokeStyle 方法创建一个新的笔划样式对象。
如果只希望使用特定类型的联接笔划,您可以创建一个笔划样式,如下所示:
D2D1_STROKE_STYLE_PROPERTIES properties = D2D1::StrokeStyleProperties();
properties.lineJoin = D2D1_LINE_JOIN_BEVEL;
HR(m_factory->CreateStrokeStyle(properties,
0, // dashes
0, // dash count
&m_strokeStyle));
D2D1_STROKE_STYLE_PROPERTIES 结构提供了多种特别控制该笔划样式的各个方面的成员,形状或首的大纲或短划线,以及短划线样式本身的每一端字下沉。 CreateStrokeStyle 方法在第二个和第三个参数是可选的您需要为它们提供仅当您正在定义一个自定义的虚线的笔划样式。 若要定义笔划的虚线的样式,请确保对在指定带短划线。 在每一对中的第一个元素是将短划线的长度第二个是空间之前将下一个短划线的长度。 值本身被乘以笔划宽度。 您可以指定任意多个对以生成所需的模式。 示例如下:
D2D1_STROKE_STYLE_PROPERTIES properties = D2D1::StrokeStyleProperties();
properties.dashStyle = D2D1_DASH_STYLE_CUSTOM;
float dashes[] =
{
2.0f, 1.0f,
3.0f, 1.0f,
};
HR(m_factory->CreateStrokeStyle(properties,
dashes,
_countof(dashes),
&m_strokeStyle));
您可以从任何不同的短划线样式而不是定义您自己的 D2D1_DASH_STYLE 枚举中的一个数。 图 4 显示一些不同的笔划样式在工作。 如果您仔细您将看到与为最佳外观结果的每个基元消除锯齿的融合 Direct2D 自动 alpha。
还有得更多的 Direct2D 可以为您执行,从复杂几何图形和转换,绘制文本和位图,和更多。 希望以覆盖这些以及更多在将来的列。
KENNY kerr 一个软件艺术家特殊化的 Windows 软件开发中。 他热衷一个写入和教学有关编程和软件设计的开发人员。 访问他在 weblogs.asp.net/kennykerr。