画笔概述

本概述介绍如何创建和使用 ID2D1SolidColorBrushID2D1LinearGradientBrushID2D1RadialGradientBrushID2D1BitmapBrush 对象来绘制纯色、渐变和位图的区域。 它包含下列部分。

先决条件

本概述假定你熟悉基本 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(Enum, 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、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
        );
    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 绘制的正方形,其中包含两种预定义颜色“黄色”和“ForestGreen”。

用黄色和森林绿色的线性渐变画笔绘制的方形图示

若要创建上图中显示的渐变,请完成以下步骤:

  1. 声明两个D2D1_GRADIENT_STOP对象。 每个渐变停止点指定一个颜色和一个位置。 0.0 的位置表示渐变的开头,而 1.0 的位置表示渐变的末尾。

    以下代码创建两个 D2D1_GRADIENT_STOP 对象的数组。 第一个标记在位置 0 处指定颜色“黄色”,第二个标记在位置 1 处指定颜色“森林绿”。

    // 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;
  1. 创建 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
        );
  1. 创建 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
            );
    }
  1. 使用 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结构的 startPointendPoint 字段指定线条的方向和大小。 创建画笔后,可以通过调用画笔的 SetStartPointSetEndPoint 方法来调整渐变轴。 通过操作画笔的开始点和结束点,可以创建水平和垂直渐变、反转渐变方向等。

例如,在下图中,起点设置为(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 绘制此圆圈,该圆圈的两个颜色停止点为:“黄色”位于 0.0 的位置,在 1.0 位置绘制“ForestGreen”。 与创建 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,传入渐变停止点数组和径向渐变画笔属性,其中 center 值设置为 (75, 75),gradientOriginOffset 设置为 (0, 0),radiusXradiusY 均设置为 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);

配置径向渐变

中心gradientOriginOffsetradiusX 和/或 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。 该方法返回 HRESULTID2D1BitmapBrush 对象。 某些 CreateBitmapBrush 重载允许你通过接受 D2D1_BRUSH_PROPERTIESD2D1_BITMAP_BRUSH_PROPERTIES 结构来指定其他选项。

if (SUCCEEDED(hr))
{
    hr = m_pRenderTarget->CreateBitmapBrush(
        m_pBitmap,
        &m_pBitmapBrush
        );
}

下一个示例使用画笔填充矩形。

m_pRenderTarget->FillRectangle(&rcBrushRect, m_pBitmapBrush);

配置扩展模式

有时,渐变画笔的渐变效果或位图画笔的位图并不能完全填满正在绘制的区域。

下图显示了 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);

它生成输出,如下图所示。

原始图像以及经过 x 方向和 y 方向镜像处理后生成的图像的插图

变换画笔

当您使用画笔绘制时,绘制会在渲染目标的坐标空间内进行。 画笔不会自动定位自身以与所绘制的对象对齐;默认情况下,它们开始绘制呈现目标的原点(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);