Direct2D Tutorial Part 4: Gradient Brush

Table of Contents

The example code is hosted at Github.

Introduction

In this article, we’ll look at how to draw with linear and radial gradient colors in Direct2D. The prerequisite of the article is the knowledge to set up the RenderTarget. If you haven’t got a clue of what RenderTarget is, please go read the RenderTarget article first and then come back to read this article.

Linear Gradient

d2d_linear_grad

To create a linear gradient brush, an array of gradient stop has to be defined. A gradient stop consists of its position and color. The first gradient stop position should be 0 and the last be 1. Then CreateGradientStopCollection() is called to create ID2D1GradientStopCollection from stops[] which is followed by CreateLinearGradientBrush() to create the ID2D1LinearGradientBrush.

ComPtr<ID2D1LinearGradientBrush> m_LinearBrush;

void CD2DGradientDlg::CreateLinearGradientBrush()
{
    D2D1_GRADIENT_STOP stops[] =
    {
        { 0.0f, ColorF(ColorF::Cyan) },
        { 1.0f, ColorF(ColorF::DarkBlue) }
    };

    ComPtr<ID2D1GradientStopCollection> collection;

    HR(m_Target->CreateGradientStopCollection(stops, _countof(stops),
        collection.GetAddressOf()));

    D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = {};

    HR(m_Target->CreateLinearGradientBrush(props, collection.Get(),
        m_LinearBrush.ReleaseAndGetAddressOf()));
}

After the gradient brush is created, we can draw with it. First, we set the start and end point with SetStartPoint() and SetEndPoint(). Alternatively, the start and end point can be specified in D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES structure during the brush creation code above. Our gradient starts from position (0,0) to a position of the dialog width and height, meaning the gradient is diagonal.

void CD2DGradientDlg::DrawLinearGradientRect()
{
    auto size = m_Target->GetSize();

	m_LinearBrush->SetStartPoint(Point2F(0.0f, 0.0f));

    m_LinearBrush->SetEndPoint(Point2F(size.width, size.height));

    auto r = RectF(0.0f, 0.0f, size.width, size.height);

    m_Target->FillRectangle(r, m_LinearBrush.Get());
}

Rainbow Linear Gradient

Next, we’ll create a horizontal linear gradient with rainbow colors (meaning more than 2 colors). The previous gradient stops are commented out and a new stops comprised of 4 colors are specified. The 2nd and 3rd stops are positioned at 0.33 and 0.66. Any colors in-between these stops are linearly interpolated.

ComPtr<ID2D1LinearGradientBrush> m_LinearBrush;

void CD2DGradientDlg::CreateLinearGradientBrush()
{
    /*
    D2D1_GRADIENT_STOP stops[] =
    {
        { 0.0f, ColorF(ColorF::Cyan) },
        { 1.0f, ColorF(ColorF::DarkBlue) }
    };
    */
    D2D1_GRADIENT_STOP stops[] =
    {
        { 0.0f, ColorF(227.0f / 255.0f, 9.0f / 255.0f, 64.0f / 255.0f, 1.0f) },
        { 0.33f, ColorF(231.0f / 255.0f, 215.0f / 255.0f, 2.0f / 255.0f, 1.0f) },
        { 0.66f, ColorF(15.0f / 255.0f, 168.0f / 255.0f, 149.0f / 255.0f, 1.0f) },
        { 1.0f, ColorF(19.0f / 255.0f, 115.0f / 255.0f, 232.0f / 255.0f, 1.0f) }
    };
    ComPtr<ID2D1GradientStopCollection> collection;

    HR(m_Target->CreateGradientStopCollection(stops, _countof(stops),
        collection.GetAddressOf()));

    D2D1_LINEAR_GRADIENT_BRUSH_PROPERTIES props = {};

    HR(m_Target->CreateLinearGradientBrush(props, collection.Get(),
        m_LinearBrush.ReleaseAndGetAddressOf()));
}

In the drawing, code remains unchanged except for the end point’s y coordinate which is changed to 0 to create a horizontal gradient.

m_LinearBrush->SetStartPoint(Point2F(0.0f, 0.0f));

m_LinearBrush->SetEndPoint(Point2F(size.width, 0.0f));

This is the rainbow gradient we created.

d2d_rainbow_linear_grad

Rainbow Linear Gradient Text

In this section, we look at how to apply the gradient brush to text. In Direct2D, the brush can be applied to any drawing whose function accepts a brush parameter. To display text with DrawText, a device independent resource, IDWriteTextFormat, has to created with CreateTextFormat() from the DirectWrite factory.

ComPtr<IDWriteTextFormat> m_TextFormat;

void CD2DAffineTransformDlg::CreateDeviceIndependentResources()
{
    HR(FactorySingleton::GetDWriteFactory()->CreateTextFormat(L"Arial Black",
        nullptr, DWRITE_FONT_WEIGHT_ULTRA_BOLD, DWRITE_FONT_STYLE_NORMAL,
        DWRITE_FONT_STRETCH_NORMAL, 40, L"",
        m_TextFormat.ReleaseAndGetAddressOf()));
}

To draw text, the FillRectangle() in the above drawing code is replaced by DrawText().

void CD2DGradientDlg::DrawLinearGradientText()
{
    auto size = m_Target->GetSize();

    m_LinearBrush->SetStartPoint(Point2F(0.0f, 0.0f));

    m_LinearBrush->SetEndPoint(Point2F(size.width, 0.0f));

    auto r = RectF(0.0f, 0.0f, size.width, size.height);

    m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), 
        &r, m_LinearBrush.Get());
}

This is gradient text output.

d2d_rainbow_linear_grad_text

Radial Gradient

d2d_radial_grad

In this section, we’ll show how to create and use a radial gradient brush. Its creation function is almost similar to the linear one, except the functions are related to radial gradient brush. Notice, in D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES, its center point is set.

ComPtr<ID2D1RadialGradientBrush> m_RadialBrush;

void CD2DGradientDlg::CreateRadialGradientBrush()
{
    D2D1_GRADIENT_STOP stops[] =
    {
        { 0.0f, ColorF(ColorF::Cyan) },
        { 1.0f, ColorF(ColorF::DarkBlue) }
    };

    ComPtr<ID2D1GradientStopCollection> collection;
    HR(m_Target->CreateGradientStopCollection(stops, _countof(stops), 
        collection.GetAddressOf()));

    D2D1_RADIAL_GRADIENT_BRUSH_PROPERTIES props = {};
    props.center = Point2F(50.0f, 50.0f);
    HR(m_Target->CreateRadialGradientBrush(props, collection.Get(), 
        m_RadialBrush.ReleaseAndGetAddressOf()));
}

Then FillRectangle() is called with the radial gradient brush.

void CD2DGradientDlg::DrawRadialGradientRect()
{
    auto size = m_Target->GetSize();
    auto radius = min(size.width, size.height);

    m_RadialBrush->SetRadiusX(radius);
    m_RadialBrush->SetRadiusY(radius);

    m_Target->FillRectangle(RectF(0.0f, 0.0f, size.width, size.height), 
        m_RadialBrush.Get());
}

Radial Gradient Text

d2d_radial_grad_text

To draw text with radial gradient brush, the code is the same as linear gradient text, except the radial gradient brush is passed to DrawText().

void CD2DGradientDlg::DrawRadialGradientText()
{
    auto size = m_Target->GetSize();
    auto radius = min(size.width, size.height);

    m_RadialBrush->SetRadiusX(radius);
    m_RadialBrush->SetRadiusY(radius);

    auto r = RectF(0.0f, 0.0f, size.width, size.height);

    m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), 
        &r, m_RadialBrush.Get());
}

Demo Code

All the code shown in the article is put in one single demo. To see certain gradient demo, just comment and uncomment the function you want to see in the Draw().

void CD2DGradientDlg::Draw()
{
    m_Target->Clear(ColorF(ColorF::White));

    //DrawLinearGradientRect();
    //DrawLinearGradientText();
    //DrawRadialGradientRect();
    DrawRadialGradientText();
}

History

  • 18th September, 2020: First release

Articles in the Series

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this:
search previous next tag category expand menu location phone mail time cart zoom edit close