Direct2D教程VI——转换(Transform)

目前博客园中成系列的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矩阵以下所示

image

M矩阵中起到决定做用是第一、2列的6个数字

 

则转换的基本计算公式是

P1=P×M

X1=X*m11+Y*m21+m31

Y1=X*m12+Y*m22+m32

 

 

平移转换(TranslateTransform)

 

以下图所示,平移转换(TranslateTransform),把T1转换到T2

image

 

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

image

image

 

 

旋转转换(RotateTransform)

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

image

假设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

image

image

 

 

缩放转换(ScaleTransform)

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

image

 

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

image

image

 

 

 

倾斜转换(SkewTransform)

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

image

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

image

image

 

 

 

在咱们常见的四种转换(平移转换(TranslateTransform)、旋转转换(RotateTransform)、缩放转换(ScaleTransform)、倾斜转换(SkewTransform))均可以用转换矩阵M来表达。

但转换矩阵的优点不只仅是四种转化。他还能够衍生出其余的转化

 

复合转换

上面介绍的四种转化都是转化基准点在原点。若是如今我有需求,绕着(2,1)这个点旋转30度的这个转换怎么办?

实际上,能够把这个转换分解成三个转换

一、先是平移转换,把(2,1)平移到(0,0)

二、再是旋转转换,绕着原点旋转30度

三、再是平移转换,把(0,0)平移到(2,1)

则这个转换矩阵M能够用三个转换的转换矩阵的连乘来表示

image

image

 

要注意的是矩阵的运算不知足交换率,即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)相似,都是经过旋转坐标轴来转换,具体的区别看下面的示意图

image

区别在于仿射转换(AffineTransform)在旋转坐标轴的时候,坐标轴上的单位长度没有发生变化(倾斜转换(SkewTransform)坐标轴的单位长度是发生变化的)。

我的以为,仿射转换(AffineTransform)比倾斜转换(SkewTransform)更加接近于真实的视觉转换

能够推导出,x2=x1·CosΘ2+y1·SinΘ1,y2=x1·SinΘ2+y1·CosΘ1。由上面的基本计算公式,能够推导出转换矩阵M

image

image

 

因为在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)上运用转换的例子。先看看准备的文件

218

这个是以前的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

看看效果图吧

image

 

效果仍是不错的吧。把本来正方形的图案转换成倾斜视角的图案。这个效果像不像一些游戏的效果?这个就是仿射转换(AffineTransform)的功劳。(倾斜转换(SkewTransform)达不到这样的效果)

 

下面再给这个画面添加一我的物,人物以下:

219

 

因为已经把坐标系改为仿射坐标,因此在绘制时,先要进行逆转换。因而用到以前的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

示例代码的效果以下:

image

 

有点游戏画面的雏形了吧。

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

相关文章
相关标签/搜索