目前博客园中成系列的Direct2D的教程有html
一、万一的 Direct2D 系列,用的是Delphi 2009windows
二、zdd的 Direct2D 系列,用的是VS中的C++函数
三、本文所在的 Direct2D教程 系列,用的是VS2010的Visual Basic语言(能够很方便的转为C#),基于Windows API Code Pack 1.1。spa
还有官方的说明文档 Direct2D ,用的是C++。.net
本系列的前几篇文章:3d
Direct2D教程I——简介及首个例子orm
Direct2D教程II——绘制基本图形和线型(StrokeStyle)的设置详解htm
Direct2D教程III——几何(Geometry)对象对象
Direct2D教程IV——笔刷(Brush)对象blog
Direct2D教程V——位图(Bitmap)和位图笔刷(BitmapBrush)
自GDI+开始,GDI+、WPF、Direct2D一路过来,转换(Transform)始终是一个重要的内容,经过转换(Transform)能实现一些特殊的效果。
转换(Transform):经过必定的运算,把一个平面坐标系的点变换为另外一个坐标系的点。
目前变换主要有平移转换(TranslateTransform)、旋转转换(RotateTransform)、缩放转换(ScaleTransform)、倾斜转换(SkewTransform)
因为图形是由点组成的,所以转换也能把图形转换成另外一个图形
转换(Transform)的矩阵知识
在.net中(包括GDI+、WPF、Direct2D),转换(Transform)是经过定义转换矩阵实现的。源点P(X,Y),经过转换(Transform)后获得目标点P1(X1,Y1),是经过转换矩阵M来实现的。把点的坐标扩成3个份量,前2个份量分别是X份量和Y份量,最后1个份量定义成1。则源点P(X,Y,1),目标点P1(X1,Y1,1)。
转换矩阵M是个3*3的矩阵,最后1列的3个数字自上而下分别是0、0、1。则M矩阵以下所示

M矩阵中起到决定做用是第一、2列的6个数字
则转换的基本计算公式是
P1=P×M
X1=X*m11+Y*m21+m31
Y1=X*m12+Y*m22+m32
平移转换(TranslateTransform)
以下图所示,平移转换(TranslateTransform),把T1转换到T2。

很容易的推导出,x2=x1+dx,y2=y1+dy。由上面的基本计算公式,能够推导出转换矩阵M


旋转转换(RotateTransform)
旋转转换(RotateTransform)以下图所示,T1绕着原点旋转Θ,转换到T2点

假设OT1的距离是R,T1和X轴的夹角是α。则x1和y1的公式为
x1=R·Cosα
y1=R·Sinα
同理的,x2和y2的公式为
x2=R·Cos(α+Θ)=R·Cosα·CosΘ-R·Sinα·SinΘ=x1·CosΘ-y1·SinΘ
y2=R·Sin(α+Θ)=R·Sinα·CosΘ+R·Cosα·SinΘ=y1·CosΘ+x1·SinΘ
由上面的基本计算公式,能够推导出转换矩阵M


缩放转换(ScaleTransform)
缩放转换(ScaleTransform)以下图所示,T1沿着X轴缩放dx倍、Y轴缩放dy倍,转换到T2点

很容易的推导出,x2=x1·dx,y2=y1·dy。由上面的基本计算公式,能够推导出转换矩阵M


倾斜转换(SkewTransform)
倾斜转换(SkewTransform)以下图所示,Y轴向右旋转Θ1到Y',X轴向下旋转Θ2到X',T1转换到T2点

在上图中的红色辅助线的帮助下,能够推导出,x2=x1+y1·TanΘ1,y2=y1+x1·TanΘ2。由上面的基本计算公式,能够推导出转换矩阵M


在咱们常见的四种转换(平移转换(TranslateTransform)、旋转转换(RotateTransform)、缩放转换(ScaleTransform)、倾斜转换(SkewTransform))均可以用转换矩阵M来表达。
但转换矩阵的优点不只仅是四种转化。他还能够衍生出其余的转化
复合转换
上面介绍的四种转化都是转化基准点在原点。若是如今我有需求,绕着(2,1)这个点旋转30度的这个转换怎么办?
实际上,能够把这个转换分解成三个转换
一、先是平移转换,把(2,1)平移到(0,0)
二、再是旋转转换,绕着原点旋转30度
三、再是平移转换,把(0,0)平移到(2,1)
则这个转换矩阵M能够用三个转换的转换矩阵的连乘来表示


要注意的是矩阵的运算不知足交换率,即M1·M2和M2·M1不必定相等
Direct2D中的转换矩阵
在Direct2D中,也是用矩阵来表示转换。转换矩阵的结构是Matrix3x2F
在上面的说明中,矩阵的最后1列是固定的三个数字(0,0,1)。所以结构Matrix3x2F在内部的实现是经过6个变量来实现的(m十一、m十二、m2一、m2二、m3一、m32)。
来看看结构Matrix3x2F的原型定义
Direct2D1.
Matrix3x2F(m11
As
Single, m12
As
Single, m21
As
Single, m22
As
Single, m31
As
Single, m32
As
Single)
Public
Shared
ReadOnly
Property Identity()
As Direct2D1.
Matrix3x2F
Public
Shared
Function Translation(x
As
Single, y
As
Single)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Translation(size
As Direct2D1.
SizeF)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Scale(x
As
Single, y
As
Single)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Scale(x
As
Single, y
As
Single, center
As Direct2D1.
Point2F)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Scale(size
As Direct2D1.
SizeF)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Scale(size
As Direct2D1.
SizeF, center
As Direct2D1.
Point2F)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Rotation(angle
As
Single)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Rotation(angle
As
Single, center
As Direct2D1.
Point2F)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Skew(angleX
As
Single, angleY
As
Single)
As Direct2D1.
Matrix3x2F
Public
Shared
Function Skew(angleX
As
Single, angleY
As
Single, center
As Direct2D1.
Point2F)
As Direct2D1.
Matrix3x2F
从上面的原型定义能够看出,结构Matrix3x2F以共享方法的形式提供了四种基本的转换。在旋转转换(RotateTransform)、缩放转换(ScaleTransform)、倾斜转换(SkewTransform)的共享方法中还提供了以不一样基准点的转换的方法。这样,就免去了本身再额外计算的过程。
不过因为没有提供矩阵乘法的函数,也就是在其余的复合转换中,只能本身进行计算。这也是这个结构的局限性。(或者微软认为,只须要这几个转换就足够了。实际上在Direct2D的基本类库中是提供了矩阵的乘法。在封装成Windows API Code Pack 1.1后,反而是取消了矩阵乘法)
GDI+、WPF中的转换矩阵
除了Direct2D中的结构Matrix3x2F外。在System.Drawing.Drawing2D空间下也提供告终构Matrix,用于GDI+和WPF中的转换矩阵。这个结构提供了三个很是有用的方法
Public
Sub Multiply(matrix
As Drawing2D.
Matrix)
Public
ReadOnly
Property IsInvertible()
As
Boolean
Public
Sub Invert()
第一个方法将指定的矩阵和自身相乘(自身在后,能够用这个函数的另外一个重载来实现矩阵的位置不一样),提供了矩阵的乘法,实现了自定义的复合转换
第二个属性是判断该矩阵是否可逆
第三个方法是对该矩阵求逆矩阵。求逆矩阵的意义在于得到逆转换。(我以为这个方法很实用,惋惜在Direct2D中没有提供这个方法)
利用结构Matrix实现Direct2D中的Matrix3x2F的矩阵乘法和逆矩阵
咱们能够利用结构Matrix来在Direct2D中实现矩阵乘法和逆矩阵,代码以下:
Public
Function Multiply(M1
As Direct2D1.
Matrix3x2F, M2
As Direct2D1.
Matrix3x2F)
As Direct2D1.
Matrix3x2F
Dim S1
As
New Drawing2D.
Matrix(M1.M11, M1.M12, M1.M21, M1.M22, M1.M31, M1.M32)
Dim S2
As
New Drawing2D.
Matrix(M2.M11, M2.M12, M2.M21, M2.M22, M2.M31, M2.M32)
S1.Multiply(S2, Drawing2D.
MatrixOrder.Append)
Dim S()
As
Single = S1.Elements
Return
New Direct2D1.
Matrix3x2F(S(0), S(1), S(2), S(3), S(4), S(5))
End
Function
Public
Function Invert(M1
As Direct2D1.
Matrix3x2F)
As Direct2D1.
Matrix3x2F
Dim S1
As
New Drawing2D.
Matrix(M1.M11, M1.M12, M1.M21, M1.M22, M1.M31, M1.M32)
If S1.IsInvertible =
True
Then S1.Invert()
Dim S()
As
Single = S1.Elements
Return
New Direct2D1.
Matrix3x2F(S(0), S(1), S(2), S(3), S(4), S(5))
End
Function
自定义的仿射转换(AffineTransform)
仿射转换(AffineTransform)和倾斜转换(SkewTransform)相似,都是经过旋转坐标轴来转换,具体的区别看下面的示意图

区别在于仿射转换(AffineTransform)在旋转坐标轴的时候,坐标轴上的单位长度没有发生变化(倾斜转换(SkewTransform)坐标轴的单位长度是发生变化的)。
我的以为,仿射转换(AffineTransform)比倾斜转换(SkewTransform)更加接近于真实的视觉转换
能够推导出,x2=x1·CosΘ2+y1·SinΘ1,y2=x1·SinΘ2+y1·CosΘ1。由上面的基本计算公式,能够推导出转换矩阵M


因为在Direct2D中没有提供仿射转换(AffineTransform)的函数,所以本身编写一个仿射转换(AffineTransform)的矩阵函数。代码以下:
Public
Function AffineMatrix(angelX
As
Single, angelY
As
Single)
As Direct2D1.
Matrix3x2F
Dim M
As
New Direct2D1.
Matrix3x2F
M.M11 =
Math.Cos(angelY *
Math.PI / 180)
M.M12 =
Math.Sin(angelY *
Math.PI / 180)
M.M21 =
Math.Sin(angelX *
Math.PI / 180)
M.M22 =
Math.Cos(angelX *
Math.PI / 180)
M.M31 = 0
M.M32 = 0
Return M
End
Function
转换(Transform)在Direct2D中运用的范围
在Direct2D中,什么对象能运用转换(Transform)?答案是不少,能够是画布(RenderTarget对象,至关于改变画布的坐标系)、笔刷(Brush对象,主要是用于位图笔刷(BitmapBrush),更改位图笔刷(BitmapBrush)的起始位置等)、形状(更改形状的外形、位置等)。几乎全部的对象都能运用转换
下面这个例子,是在画布(RenderTarget)上运用转换的例子。先看看准备的文件

这个是以前的216.png,如今加上一圈红边后,保存为218.png。
Public
Class
clsDirect2DSample14
Inherits
clsDirect2DSample11
Public
Shadows
Sub Render()
If
Not _renderTarget
Is
Nothing
Then
With _renderTarget
.BeginDraw()
.Clear(
New Direct2D1.
ColorF(
Color.Chocolate.ToArgb))
Dim B
As Direct2D1.
D2DBitmap = LoadBitmapFromFile(
"218.png")
Dim BB
As Direct2D1.
BitmapBrush = _renderTarget.CreateBitmapBrush(B)
BB.Transform = Direct2D1.
Matrix3x2F.Scale(0.25, 0.25)
BB.ExtendModeX = Direct2D1.
ExtendMode.Wrap
BB.ExtendModeY = Direct2D1.
ExtendMode.Wrap
.Transform = AffineMatrix(60, -30)
Dim R
As
New Direct2D1.
RectF(-195, 195, 65, 455)
Dim SB
As Direct2D1.
SolidColorBrush = _renderTarget.CreateSolidColorBrush(
New Direct2D1.
ColorF(0, 0, 0))
.DrawRectangle(R, SB, 3)
.FillRectangle(R, BB)
.EndDraw()
End
With
End
If
End
Sub
Public
Function AffineMatrix(angelX
As
Single, angelY
As
Single)
As Direct2D1.
Matrix3x2F
Dim M
As
New Direct2D1.
Matrix3x2F
M.M11 =
Math.Cos(angelY *
Math.PI / 180)
M.M12 =
Math.Sin(angelY *
Math.PI / 180)
M.M21 =
Math.Sin(angelX *
Math.PI / 180)
M.M22 =
Math.Cos(angelX *
Math.PI / 180)
M.M31 = 0
M.M32 = 0
Return M
End
Function
Public
Function Multiply(M1
As Direct2D1.
Matrix3x2F, M2
As Direct2D1.
Matrix3x2F)
As Direct2D1.
Matrix3x2F
Dim S1
As
New Drawing2D.
Matrix(M1.M11, M1.M12, M1.M21, M1.M22, M1.M31, M1.M32)
Dim S2
As
New Drawing2D.
Matrix(M2.M11, M2.M12, M2.M21, M2.M22, M2.M31, M2.M32)
S1.Multiply(S2, Drawing2D.
MatrixOrder.Append)
Dim S()
As
Single = S1.Elements
Return
New Direct2D1.
Matrix3x2F(S(0), S(1), S(2), S(3), S(4), S(5))
End
Function
Public
Function Invert(M1
As Direct2D1.
Matrix3x2F)
As Direct2D1.
Matrix3x2F
Dim S1
As
New Drawing2D.
Matrix(M1.M11, M1.M12, M1.M21, M1.M22, M1.M31, M1.M32)
If S1.IsInvertible =
True
Then S1.Invert()
Dim S()
As
Single = S1.Elements
Return
New Direct2D1.
Matrix3x2F(S(0), S(1), S(2), S(3), S(4), S(5))
End
Function
End
Class
看看效果图吧

效果仍是不错的吧。把本来正方形的图案转换成倾斜视角的图案。这个效果像不像一些游戏的效果?这个就是仿射转换(AffineTransform)的功劳。(倾斜转换(SkewTransform)达不到这样的效果)
下面再给这个画面添加一我的物,人物以下:

因为已经把坐标系改为仿射坐标,因此在绘制时,先要进行逆转换。因而用到以前的Invert(逆转换函数)和Multiply(矩阵乘法,实现复合转换)。代码以下:
Public
Class
clsDirect2DSample15
Inherits
clsDirect2DSample14
Public
Shadows
Sub Render()
If
Not _renderTarget
Is
Nothing
Then
With _renderTarget
.BeginDraw()
.Clear(
New Direct2D1.
ColorF(
Color.Chocolate.ToArgb))
Dim B
As Direct2D1.
D2DBitmap = LoadBitmapFromFile(
"218.png")
Dim Man
As Direct2D1.
D2DBitmap = LoadBitmapFromFile(
"219.png")
Dim BB
As Direct2D1.
BitmapBrush = _renderTarget.CreateBitmapBrush(B)
BB.Transform = Direct2D1.
Matrix3x2F.Scale(0.25, 0.25)
BB.ExtendModeX = Direct2D1.
ExtendMode.Wrap
BB.ExtendModeY = Direct2D1.
ExtendMode.Wrap
.Transform = AffineMatrix(60, -30)
Dim R
As
New Direct2D1.
RectF(-195, 195, 65, 455)
Dim SB
As Direct2D1.
SolidColorBrush = _renderTarget.CreateSolidColorBrush(
New Direct2D1.
ColorF(0, 0, 0))
.DrawRectangle(R, SB, 3)
.FillRectangle(R, BB)
PaintMan(Man,
New Direct2D1.
Point2F(40, 140), .Transform)
.EndDraw()
End
With
End
If
End
Sub
Public
Sub PaintMan(man
As Direct2D1.
D2DBitmap, point
As Direct2D1.
Point2F, T
As Direct2D1.
Matrix3x2F)
Dim F
As
New Direct2D1.
RectF(point.X - 200, point.Y - 200, man.PixelSize.Width + point.X + 200, man.PixelSize.Height + point.Y + 200)
Dim B
As Direct2D1.
BitmapBrush = _renderTarget.CreateBitmapBrush(man)
B.Transform = Multiply(Invert(T), Direct2D1.
Matrix3x2F.Translation(point.X, point.Y))
_renderTarget.FillRectangle(F, B)
End
Sub
End
Class
示例代码的效果以下:

有点游戏画面的雏形了吧。
不过,这个代码仅仅是举例,来讲明转换(Transform)的效果。实际上,背景和人物放在不一样的图层里来显示,可能代码会简单一些。可是,绘制在一个图层里,能够作到坐标的统一,不须要进行坐标的转换。就看你的取舍了。