画笔概述
本概述介绍如何创建和使用 ID2D1SolidColorBrush、 ID2D1LinearGradientBrush、 ID2D1RadialGradientBrush 和 ID2D1BitmapBrush 对象来绘制具有纯色、渐变和位图的区域。 其中包含以下各节。
先决条件
本概述假定你熟悉基本 Direct2D 应用程序的结构,如 创建简单的 Direct2D 应用程序中所述。
画笔类型
画笔使用其输出“绘制”区域。 不同的画笔具有不同的输出类型。 Direct2D 提供四种画笔类型: ID2D1SolidColorBrush 使用纯色绘制区域、使用线性渐变绘制 ID2D1LinearGradientBrush 、具有径向渐变的 ID2D1RadialGradientBrush 和具有位图的 ID2D1BitmapBrush 。
注意
从Windows 8开始,还可以使用 ID2D1ImageBrush,这类似于位图画笔,但也可以使用基元。
所有画笔继承自 ID2D1Brush ,并共享一组常见功能, (设置和获取不透明度,以及转换画笔) ;它们由 ID2D1RenderTarget 创建,并且是依赖于设备的资源:应用程序应在初始化将使用画笔的呈现目标后创建画笔,并在需要重新创建呈现目标时重新创建画笔。 (有关资源的详细信息,请参阅 资源概述。)
下图显示了每种不同画笔类型的示例。
颜色基础知识
在使用 ID2D1SolidColorBrush 或渐变画笔进行绘制之前,需要选择颜色。 在 Direct2D 中,颜色由 D2D1_COLOR_F 结构 (实际上只是 Direct3D、 D3DCOLORVALUE) 所使用的结构的新名称。
在Windows 8之前,D2D1_COLOR_F使用 sRGB 编码。 sRGB 编码将颜色分为四个部分:红色、绿色、蓝色和 alpha。 每个分量均由一个浮点值表示,该值通常介于 0.0 和 1.0 之间。 值 0.0 表示完全没有该颜色,而值 1.0 表示该颜色完全呈现。 对于 alpha 分量,0.0 表示完全透明的颜色,而 1.0 表示完全不透明的颜色。
从 Windows 8 开始,D2D1_COLOR_F也接受 scRGB 编码。 scRGB 是 的超集,允许颜色值高于 1.0 和低于 0.0。
若要定义颜色,可以使用 D2D1_COLOR_F 结构并自行初始化其字段,也可以使用 D2D1::ColorF 类来帮助创建颜色。 ColorF 类提供了多个用于定义颜色的构造函数。 如果未在构造函数中指定 alpha 值,则默认为 1.0。
使用 ColorF (枚举、FLOAT) 构造函数指定预定义的颜色和 alpha 通道值。 alpha 通道值的范围从 0.0 到 1.0,其中 0.0 表示完全透明的颜色,1.0 表示完全不透明的颜色。 下图显示了多种预定义颜色及其十六进制等效项。 有关预定义颜色的完整列表,请参阅 ColorF 类的 Color 常量部分。
以下示例创建预定义颜色,并使用它指定 ID2D1SolidColorBrush 的颜色。
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
&m_pBlackBrush
);
使用 ColorF (FLOAT、FLOAT、FLOAT、FLOAT) 构造函数指定红色、绿色、蓝色和 alpha 序列中的颜色,其中每个元素的值介于 0.0 和 1.0 之间。
以下示例指定颜色的红色、绿色、蓝色和 alpha 值。
ID2D1SolidColorBrush *pGridBrush = NULL;
hr = pCompatibleRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0.93f, 0.94f, 0.96f, 1.0f)),
&pGridBrush
);
- 使用 ColorF (UINT32、FLOAT) 构造函数指定颜色的十六进制值和 alpha 值,如以下示例所示。
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),
&m_pYellowGreenBrush
);
Alpha 模式
无论使用画笔的呈现目标的 alpha 模式如何, D2D1_COLOR_F 值始终被解释为直 alpha。
使用纯色画笔
若要创建纯色画笔,请调用 ID2D1RenderTarget::CreateSolidColorBrush 方法,该方法返回 HRESULT 和 ID2D1SolidColorBrush 对象。 下图显示了一个正方形,它用黑色画笔笔划,并使用颜色值为 0x9ACD32 的纯色画笔绘制。
以下代码演示如何创建和使用颜色值为 0x9ACD32 的黑色画笔和画笔来填充和绘制此方形。
ID2D1SolidColorBrush *m_pBlackBrush;
ID2D1SolidColorBrush *m_pYellowGreenBrush;
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Black, 1.0f),
&m_pBlackBrush
);
}
// Create a solid color brush with its rgb value 0x9ACD32.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF(0x9ACD32, 1.0f)),
&m_pYellowGreenBrush
);
}
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pYellowGreenBrush);
m_pRenderTarget->DrawRectangle(&rcBrushRect, m_pBlackBrush, 1, NULL);
与其他画笔不同,创建 ID2D1SolidColorBrush 是一种相对廉价的操作。 每次呈现时,都可以创建 ID2D1SolidColorBrush 对象,但对性能影响甚微。 不建议对渐变画笔或位图画笔使用此方法。
使用线性渐变画笔
ID2D1LinearGradientBrush 使用沿线条(渐变轴)定义的线性渐变绘制区域。 使用 ID2D1GradientStop 对象指定渐变颜色及其沿渐变轴的位置。 还可以修改渐变轴,使你能够创建水平和垂直渐变,并反转渐变方向。 若要创建线性渐变画笔,请调用 ID2D1RenderTarget::CreateLinearGradientBrush 方法。
下图显示了使用 ID2D1LinearGradientBrush 绘制的正方形,该 ID2D1LinearGradientBrush 具有两种预定义颜色“黄色”和“ForestGreen”。
若要创建上图中显示的渐变,请完成以下步骤:
声明两 个D2D1_GRADIENT_STOP 对象。 每个渐变停止点指定一种颜色和一个位置。 位置 0.0 表示渐变的开始,而位置 1.0 表示渐变的结束。
以下代码创建包含两个 D2D1_GRADIENT_STOP 对象的数组。 第一个停靠点指定位置 0 处的颜色“黄色”,第二个停靠点指定位置 1 处的颜色“ForestGreen”。
// Create an array of gradient stops to put in the gradient stop
// collection that will be used in the gradient brush.
ID2D1GradientStopCollection *pGradientStops = NULL;
D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
- 创建 ID2D1GradientStopCollection。 以下示例调用 CreateGradientStopCollection,传入 D2D1_GRADIENT_STOP 对象的数组,渐变停止数 (2) , D2D1_GAMMA_2_2 用于内插, D2D1_EXTEND_MODE_CLAMP 扩展模式。
// Create the ID2D1GradientStopCollection from a previously
// declared array of D2D1_GRADIENT_STOP structs.
hr = m_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
);
- 创建 ID2D1LinearGradientBrush。 下一个示例调用 CreateLinearGradientBrush 方法,并向它传递线性渐变画笔属性,这些属性包含起点在 (0, 0) ,终点位于 (150,150) ,以及上一步中创建的渐变停止点。
// The line that determines the direction of the gradient starts at
// the upper-left corner of the square and ends at the lower-right corner.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateLinearGradientBrush(
D2D1::LinearGradientBrushProperties(
D2D1::Point2F(0, 0),
D2D1::Point2F(150, 150)),
pGradientStops,
&m_pLinearGradientBrush
);
}
- 使用 ID2D1LinearGradientBrush。 下一个代码示例使用画笔填充矩形。
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pLinearGradientBrush);
有关渐变停止点的详细信息
D2D1_GRADIENT_STOP是渐变画笔的基本构建基块。 渐变停止指定沿渐变轴的颜色和位置。 渐变位置的值介于 0.0 和 1.0 之间。 越接近 0.0,颜色越接近渐变的开头;越接近 1.0,颜色越接近渐变的末尾。
下图突出显示了渐变停止点。 圆圈标记渐变停止的位置,虚线显示渐变轴。
第一个渐变停止点指定 0.0 位置的黄色。 第二个渐变停止点指定 0.25 位置的红色。 从左到右沿渐变轴,这两个停止之间的颜色逐渐从黄色更改为红色。 第三个渐变停止点指定 0.75 位置的蓝色。 第二个和第三个渐变停止之间的颜色逐渐从红色更改为蓝色。 第四个渐变停止点指定 1.0 位置处的石灰绿色。 第三个和第四个渐变停止之间的颜色逐渐从蓝色更改为石灰绿色。
渐变轴
如前所述,线性渐变画笔的渐变停止沿渐变轴线定位。 创建线性渐变画笔时,可以使用D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES结构的 startPoint 和 endPoint 字段指定线条的方向和大小。 创建画笔后,可以通过调用画笔的 SetStartPoint 和 SetEndPoint 方法调整渐变轴。 通过操作画笔的起点和终点,可以创建水平和垂直渐变、反转渐变方向等。
例如,在下图中,起点设置为 (0,0) ,终点设置为 (150, 50) ;这会创建一个从左上角开始并延伸到正在绘制区域的右下角的对角渐变。 将起点设置为 (0、25) ,并将终点设置为 (150、25) 时,将创建水平渐变。 同样,将起点设置为 (75, 0) ,将终点设置为 (75, 50) 将创建垂直渐变。 将起点设置为 (0, 50) ,并将终点设置为 (150, 0) 将创建一个从左下角开始并延伸到所绘制区域的右上角的对角渐变。
使用径向渐变画笔
与沿渐变轴混合两种或更多颜色的 ID2D1LinearGradientBrush 不同, ID2D1RadialGradientBrush 绘制具有径向渐变的区域,该渐变在椭圆中混合两种或更多颜色。 ID2D1LinearGradientBrush 定义具有起点和终点的渐变轴,而 ID2D1RadialGradientBrush 则通过指定中心、水平和垂直半径以及渐变原点偏移量来定义其渐变椭圆。
与 ID2D1LinearGradientBrush 一样, ID2D1RadialGradientBrush 使用 ID2D1GradientStopCollection 来指定渐变中的颜色和位置。
下图显示了用 ID2D1RadialGradientBrush 绘制的圆圈。 圆有两个渐变停止点:第一个指定 0.0 位置的预定义颜色“黄色”,第二个指定 1.0 位置的预定义颜色“ForestGreen”。 渐变的中心 (为 75,75) ,渐变原点偏移量为 (0,0) ,x 和 y 半径为 75。
下面的代码示例演示如何使用 ID2D1RadialGradientBrush 绘制此圆圈,该 ID2D1RadialGradientBrush 具有两个颜色停止点:“黄色”位于 0.0 的位置,“ForestGreen”位于 1.0 位置。 与创建 ID2D1LinearGradientBrush 类似,该示例调用 CreateGradientStopCollection 以从渐变停止点数组创建 ID2D1GradientStopCollection 。
// Create an array of gradient stops to put in the gradient stop
// collection that will be used in the gradient brush.
ID2D1GradientStopCollection *pGradientStops = NULL;
D2D1_GRADIENT_STOP gradientStops[2];
gradientStops[0].color = D2D1::ColorF(D2D1::ColorF::Yellow, 1);
gradientStops[0].position = 0.0f;
gradientStops[1].color = D2D1::ColorF(D2D1::ColorF::ForestGreen, 1);
gradientStops[1].position = 1.0f;
// Create the ID2D1GradientStopCollection from a previously
// declared array of D2D1_GRADIENT_STOP structs.
hr = m_pRenderTarget->CreateGradientStopCollection(
gradientStops,
2,
D2D1_GAMMA_2_2,
D2D1_EXTEND_MODE_CLAMP,
&pGradientStops
);
若要创建 ID2D1RadialGradientBrush,请使用 ID2D1RenderTarget::CreateRadialGradientBrush 方法。 CreateRadialGradientBrush 采用三个参数。 第一个参数 D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES 指定渐变的中心、渐变原点偏移量以及渐变的水平和垂直半径。 第二个参数是描述颜色及其在渐变中的位置的 ID2D1GradientStopCollection ,第三个参数是接收新的 ID2D1RadialGradientBrush 引用的指针的地址。 某些重载采用附加参数、 D2D1_BRUSH_PROPERTIES 结构,用于指定要应用于新画笔的不透明度值和转换。
下一个示例调用 CreateRadialGradientBrush,传入渐变停止点数组,并将 中心 值设置为 (75、75) 的径向渐变画笔属性, 将 gradientOriginOffset 设置为 (0, 0) , radiusX 和 radiusY 都设置为 75。
// The center of the gradient is in the center of the box.
// The gradient origin offset was set to zero(0, 0) or center in this case.
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateRadialGradientBrush(
D2D1::RadialGradientBrushProperties(
D2D1::Point2F(75, 75),
D2D1::Point2F(0, 0),
75,
75),
pGradientStops,
&m_pRadialGradientBrush
);
}
最后一个示例使用画笔填充椭圆形。
m_pRenderTarget->FillEllipse(ellipse, m_pRadialGradientBrush);
m_pRenderTarget->DrawEllipse(ellipse, m_pBlackBrush, 1, NULL);
配置径向渐变
center、gradientOriginOffset、radiusX 和/或 radiusY 的不同值会产生不同的渐变。 下图显示了具有不同渐变原点偏移的几个径向渐变,从而从不同角度照亮圆圈的光的外观。
使用位图画笔
ID2D1BitmapBrush 使用由 ID2D1Bitmap 对象) 表示的位图 (绘制区域。
下图显示了用植物位图绘制的正方形。
以下示例演示如何使用 ID2D1BitmapBrush 绘制此正方形。
第一个示例初始化 ID2D1Bitmap 以用于画笔。 ID2D1Bitmap 由示例中其他位置定义的帮助程序方法 LoadResourceBitmap 提供。
// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
hr = LoadResourceBitmap(
m_pRenderTarget,
m_pWICFactory,
L"FERN",
L"Image",
&m_pBitmap
);
}
若要创建位图画笔,请调用 ID2D1RenderTarget::CreateBitmapBrush 方法并指定要绘制的 ID2D1Bitmap 。 方法返回 HRESULT 和 ID2D1BitmapBrush 对象。 某些 CreateBitmapBrush 重载允许通过接受 D2D1_BRUSH_PROPERTIES 和 D2D1_BITMAP_BRUSH_PROPERTIES 结构来指定其他选项。
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateBitmapBrush(
m_pBitmap,
&m_pBitmapBrush
);
}
下一个示例使用画笔填充矩形。
m_pRenderTarget->FillRectangle(&rcBrushRect, m_pBitmapBrush);
配置扩展模式
有时,渐变画笔的渐变或位图画笔的位图不会完全填充要绘制的区域。
当 ID2D1BitmapBrush 发生这种情况时,Direct2D 使用画笔的水平 (SetExtendModeX) 和垂直 (SetExtendModeY) 扩展模式设置来确定如何填充剩余区域。
当渐变画笔发生此情况时,Direct2D 将使用调用 CreateGradientStopCollection 创建渐变画笔的 ID2D1GradientStopCollection 时指定的 D2D1_EXTEND_MODE 参数的值来确定如何填充剩余区域。
下图显示了 ID2D1BitmapBrush 扩展模式的每个可能组合的结果: D2D1_EXTEND_MODE_CLAMP ( CLAMP) 、 D2D1_EXTEND_MODE_WRAP (WRAP) 和 D2D1_EXTEND_MIRROR (MIRROR) 。
以下示例演示如何将位图画笔的 x 和 y 扩展模式设置为 D2D1_EXTEND_MIRROR。 然后,它使用 ID2D1BitmapBrush 绘制矩形。
m_pBitmapBrush->SetExtendModeX(D2D1_EXTEND_MODE_MIRROR);
m_pBitmapBrush->SetExtendModeY(D2D1_EXTEND_MODE_MIRROR);
m_pRenderTarget->FillRectangle(exampleRectangle, m_pBitmapBrush);
它生成输出,如下图所示。
转换画笔
使用画笔绘制时,它会在呈现目标的坐标空间中绘制。 画笔不会自动定位自身以与正在绘制的对象对齐;默认情况下,它们开始在呈现目标 (0, 0) 的原点进行绘制。
可以通过设置 ID2D1LinearGradientBrush 定义的渐变“移动”到目标区域。 同样,可以通过更改 ID2D1RadialGradientBrush 定义的渐变的中心和半径来移动该渐变。
若要将 ID2D1BitmapBrush 的内容与要绘制的区域对齐,可以使用 SetTransform 方法将位图转换为所需位置。 此转换仅影响画笔;它不会影响呈现器目标绘制的任何其他内容。
下图显示了使用 ID2D1BitmapBrush 填充位于 (100, 100) 的矩形的效果。 左侧插图显示了在不转换画笔的情况下填充矩形的结果:位图绘制在呈现目标的原点处。 因此,矩形中只显示一部分位图。 右侧的插图显示了转换 ID2D1BitmapBrush 的结果,以便其内容向右移动 50 像素,向下移动 50 像素。 位图现在填充矩形。
以下代码演示如何实现此目的。 首先对 ID2D1BitmapBrush 应用平移,沿 x 轴向右移动画笔 50 像素,沿 y 轴向下移动 50 像素。 然后使用 ID2D1BitmapBrush 填充左上角位于 (100,100) ,右下角位于 (200, 200) 的矩形。
// Create the bitmap to be used by the bitmap brush.
if (SUCCEEDED(hr))
{
hr = LoadResourceBitmap(
m_pRenderTarget,
m_pWICFactory,
L"FERN",
L"Image",
&m_pBitmap
);
}
if (SUCCEEDED(hr))
{
hr = m_pRenderTarget->CreateBitmapBrush(
m_pBitmap,
&m_pBitmapBrush
);
}
D2D1_RECT_F rcTransformedBrushRect = D2D1::RectF(100, 100, 200, 200);
// Demonstrate the effect of transforming a bitmap brush.
m_pBitmapBrush->SetTransform(
D2D1::Matrix3x2F::Translation(D2D1::SizeF(50,50))
);
// To see the content of the rcTransformedBrushRect, comment
// out this statement.
m_pRenderTarget->FillRectangle(
&rcTransformedBrushRect,
m_pBitmapBrush
);
m_pRenderTarget->DrawRectangle(rcTransformedBrushRect, m_pBlackBrush, 1, NULL);