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)的效果。實際上,背景和人物放在不一樣的圖層裏來顯示,可能代碼會簡單一些。可是,繪製在一個圖層裏,能夠作到座標的統一,不須要進行座標的轉換。就看你的取捨了。

相關文章
相關標籤/搜索