Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Direct2D Tutorial Part 4: Gradient Brush

5.00/5 (4 votes)
17 Sep 2020CPOL3 min read 9.9K   291  
Direct2D Tutorial on Linear and Radial Gradient Brush
In this tutorial, we are going to take a look at drawing with linear and radial 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

Linear Gradient Image

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.

C++
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.

C++
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.

C++
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.

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

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

This is the rainbow gradient we created.

Rainbow Linear Gradient Image

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.

C++
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().

C++
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.

Rainbow Linear Gradient Text Image

Radial Gradient

Radial Gradient Image

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.

C++
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.

C++
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

Radial Gradient Text Image

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().

C++
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().

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

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

History

  • 18th September, 2020: First release

Articles in the Series

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)