Click here to Skip to main content
15,881,938 members
Articles / Programming Languages / C++

Direct2D Tutorial Part 4: Gradient Brush

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
17 Sep 2020CPOL3 min read 9K   276   11  
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)


Written By
Software Developer (Senior)
Singapore Singapore
Shao Voon is from Singapore. His interest lies primarily in computer graphics, software optimization, concurrency, security, and Agile methodologies.

In recent years, he shifted focus to software safety research. His hobby is writing a free C++ DirectX photo slideshow application which can be viewed here.

Comments and Discussions

 
-- There are no messages in this forum --