Direct2D 教程 第 3 部分:仿射变换






4.82/5 (7投票s)
关于在绘图上应用仿射变换的 Direct2D 教程
目录
示例代码托管在 Github 上。
引言
在本文中,我们将了解如何在 Direct2D 中使用矩阵进行仿射变换。 变换必须在任何要转换的绘图之前设置。 本文的先决条件是具备设置 RenderTarget
的知识。 如果您不知道 RenderTarget
是什么,请先阅读 RenderTarget 文章,然后再回来阅读本文。
文本格式
要在 Direct2D 中显示文本,必须首先创建 IDWriteTextFormat
。 IDWriteTextFormat
是一种设备无关的资源,在 CreateDeviceIndependentResources()
中创建,而 CreateDeviceIndependentResources()
又由 OnInitDialog()
或您可能拥有的任何初始化函数调用一次。 当 Direct2D 目标丢失时,无需重新创建设备无关的资源。 IDWriteTextFormat
及其创建函数 CreateTextFormat()
将在未来的文本绘图教程中进行更详细的检查。
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()));
}
转换
在本节中,文本沿 y 轴向下平移(或移动)50 像素。 在使用我们的平移矩阵设置 SetTransform()
之前,必须使用 IdentityMatrix()
调用它,以重置在此阶段之前设置的任何矩阵。 平移矩阵由 Matrix3x2F::Translation()
创建。 Matrix3x2F::Translation()
的 2 个参数是要从当前点“移动”的 X 和 Y 坐标量。 然后,m_FillBrush
设置为 Direct2D 预定义的黑色。 您可以选择其他预定义或自定义颜色。 要定义要在 SetColor()
中使用的自定义颜色,请按 RGBA
顺序给出 ColorF()
4 个浮点数。 然后,要绘制文本,在 m_Target
上调用 DrawTextW
。
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());
倾斜
接下来,我们将看到如何进行倾斜以使文本看起来像斜体,然后进行相同的平移。 前一节中的 GetClientRect()
代码未显示,因为它们没有改变。 变换通过矩阵乘法链接在一起。 您会看到矩阵乘法的顺序在这里非常重要。 矩阵乘法的顺序必须与所需变换的顺序相反。 例如,我们希望倾斜是第一个变换,然后在矩阵乘法中,则倾斜矩阵必须是最后一个操作数。 平移矩阵反之亦然。 见倾斜矩阵由 Matrix3x2F::Skew()
创建。 Matrix3x2F::Skew()
的前 2 个参数是沿 x 轴和 y 轴倾斜的角度(度),最后一个参数是倾斜的中心点。
注意:上面讨论了平移矩阵的创建。
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());
旋转
在本节中,文本向上旋转 -10
度。 倾斜和旋转必须在平移之前执行,因此矩阵乘法是 trans * rotate * skew
。 旋转矩阵由 Matrix3x2F::Rotation()
创建,该矩阵以角度作为其第一个参数。 可选的中心点参数默认为 0,0
,如果省略。
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
缩放通常用于在计算机绘图中实现放大和缩小。 在这最后一节中,我们将缩小文本绘图(使其更小)。 缩放必须在所有其他变换之前执行,因此它是矩阵乘法中的最后一个操作数。 见缩放矩阵由 Matrix3x2F::Scale()
创建,它有 2 个参数来指定 x 轴和 y 轴的缩放比例。
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());
矩阵乘法的顺序
倾斜部分提到矩阵乘法的顺序必须与所需变换的顺序相反。 应该有一个更完整的解释的部分。 看一下由两个矩阵 T
和 S
表示的两个变换,其中 T
变换首先应用于 V
,然后是 S
变换。 V
是一个向量,由要变换的 x
和 y
的笛卡尔坐标组成。
S(T(V))
注意:矩阵乘法是非交换的,因此必须尊重乘法的顺序。 虽然 T
首先应用,但 S
首先出现在等式中。
ST != TS
历史
- 2021 年 1 月 28 日:添加了矩阵乘法的顺序以进行完整的解释。
- 2020 年 8 月 20 日:初始版本