## Table of Content

The example code is hosted at Github.

## Introduction

In this article, we’ll look at how to do affine transform using matrices in Direct2D. The transformation has to be set up before any drawing that is to be transformed. The prerequisite of the article is the knowledge to set up the RenderTarget. If you haven’t got a clue what RenderTarget is, please go read the RenderTarget article first and then come back to read this article.

## Text Format

To display the text in Direct2D, `IDWriteTextFormat`

has to be created first. `IDWriteTextFormat`

is a device-independent resource that is created in `CreateDeviceIndependentResources()`

which is in turn called once by `OnInitDialog()`

or in any initialization function you may have. A device-independent resource does not need to be recreated when the Direct2D target is lost. `IDWriteTextFormat`

and its creation function, `CreateTextFormat()`

shall be examined in more detail in a future text drawing tutorial.

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())); }

## Translation

In this section, the text is translated (or moved) downward along the y-axis by 50 pixels. Before `SetTransform()`

is set with our translation matrix, it has to be called with `IdentityMatrix()`

to reset whatever matrix set prior to this stage. The translate matrix is created from `Matrix3x2F::Translation()`

. The `Matrix3x2F::Translation()`

‘s 2 parameters are the amount of X and Y coordinates to ‘move’ from the current point. Then `m_FillBrush`

is set to a Direct2D predefined black color. You can choose other predefined or custom colors. To define a custom color to be used in `SetColor()`

, give `ColorF()`

4 floating-point numbers in `RGBA`

order. Then to draw the text, `DrawTextW`

is called on the `m_Target`

.

CRect rectClient; GetClientRect(&rectClient); auto rect = RectF(0.0f, 0.0f, rectClient.Width(), rectClient.Height()); m_Target->SetTransform(D2D1::IdentityMatrix()); D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(0, 50); m_Target->SetTransform(trans); m_FillBrush->SetColor(ColorF(ColorF::Black)); m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), &rect, m_FillBrush.Get());

## Skew

Next, we’ll see how to do a skew to make the text look italic and then followed by the same translation. `GetClientRect()`

code from the previous section is not shown as they are unchanged. The transformations are chained by the matrix multiplication. You see the order of matrix multiplication is very important here. The order of matrix multiplication must be the opposite of the order of the desired transformation. For instance, we want the skew to be the first transformation, then in the matrix multiplication, then the skew matrix has to be the last operand. Vice-versa for the translation matrix. See the skew matrix is created from `Matrix3x2F::Skew()`

. The `Matrix3x2F::Skew()`

‘s first 2 parameters are the angle(degree) to skew the along the x-axis and y-axis and the last parameter is the center point of skew. **Note:** The translation matrix creation is discussed above.

m_Target->SetTransform(D2D1::IdentityMatrix()); D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(0, 50); D2D1::Matrix3x2F skew = D2D1::Matrix3x2F::Skew(-10.0f, 0.0f, Point2F(rectClient.Width() / 2, rectClient.Height() / 2)); m_Target->SetTransform(trans * skew); m_FillBrush->SetColor(ColorF(ColorF::Black)); m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), &rect, m_FillBrush.Get());

## Rotation

In this section, the text is rotated upward by `-10`

degrees. The skew and rotation has to be performed before translation, therefore the matrix multiplication is `trans * rotate * skew`

. The rotate matrix is created from `Matrix3x2F::Rotation()`

which has an angle as its first parameter. The optional center point parameter defaults to `0,0`

when left out.

m_Target->SetTransform(D2D1::IdentityMatrix()); D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(0, 50); D2D1::Matrix3x2F rotate = D2D1::Matrix3x2F::Rotation(-10.0f); D2D1::Matrix3x2F skew = D2D1::Matrix3x2F::Skew(-10.0f, 0.0f, Point2F(rectClient.Width() / 2, rectClient.Height() / 2)); m_Target->SetTransform(trans * rotate * skew); m_FillBrush->SetColor(ColorF(ColorF::Black)); m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), &rect, m_FillBrush.Get());

## Scale

Scaling is normally used to implement zoom-in and zoom-out in computer drawing. In this last section, we’ll scale down the text drawing(make smaller). Scale has to be performed before all other transformation, so it is the last operand in the matrix multiplication. See the scale matrix is created from `Matrix3x2F::Scale()`

which has 2 parameters to specify the scaling in the x-axis and y-axis.

m_Target->SetTransform(D2D1::IdentityMatrix()); D2D1::Matrix3x2F trans = D2D1::Matrix3x2F::Translation(0, 50); D2D1::Matrix3x2F rotate = D2D1::Matrix3x2F::Rotation(-10.0f); D2D1::Matrix3x2F skew = D2D1::Matrix3x2F::Skew(-10.0f, 0.0f, Point2F(rectClient.Width() / 2, rectClient.Height() / 2)); D2D1::Matrix3x2F scale = D2D1::Matrix3x2F::Scale(0.6f, 0.6f); m_Target->SetTransform(trans * rotate * skew * scale); m_FillBrush->SetColor(ColorF(ColorF::Black)); m_Target->DrawTextW((LPCTSTR)m_Text, m_Text.GetLength(), m_TextFormat.Get(), &rect, m_FillBrush.Get());

## Other articles in the series

- Direct2D Tutorial Part 1: RenderTarget
- Direct2D Tutorial Part 2: Basic Shapes
- Direct2D Tutorial Part 3: Affine Transforms

## Leave a Reply