轉自
SkiaSharp 中的矩陣轉換
利用多樣的轉換矩陣深入了解 SkiaSharp 轉換
應用於該對象的所有轉換 SKCanvas
都在結構的單個實例中合並 SKMatrix
。 這是標准的 3 x 3 變換矩陣,與所有新式2D 圖形系統中的矩陣類似。
正如您所看到的,可以在 SkiaSharp 中使用轉換,而無需知道轉換矩陣,但是轉換矩陣對於理論觀點非常重要,在使用變換來修改路徑或處理復雜的觸摸輸入時,這兩種情況都非常重要。
SKCanvas
通過訪問只讀屬性,可以隨時使用應用於的當前轉換矩陣 TotalMatrix
。 您可以使用方法設置新的轉換矩陣 SetMatrix
,還可以通過調用將該轉換矩陣還原為默認值 ResetMatrix
。
SKCanvas
直接使用畫布的矩陣轉換的另一個成員是 Concat
通過將兩個矩陣相乘在一起來連接兩個矩陣。
默認轉換矩陣為恆等矩陣,其中包含對角單元中的1個,其他所有位置均為0。
| 1 0 0 | | 0 1 0 | | 0 0 1 |
您可以使用靜態方法創建一個標識矩陣 SKMatrix.MakeIdentity
:
SKMatrix matrix = SKMatrix.MakeIdentity();
SKMatrix
默認構造函數不 not 返回恆等矩陣。 它將返回一個矩陣,其中所有單元格均設置為零。 SKMatrix
除非你計划手動設置這些單元格,否則不要使用構造函數。
1,這里講默認轉換,變換后結果不變。
將第三列設為1,等於軸永遠是1,弱化為平面。
矩陣乘法就是把第一個矩陣的橫和第二個矩陣的豎排相應位相稱,將結果相加。
當 SkiaSharp 呈現圖形對象時,每個點 (x,y) 會有效地轉換為第三列中的 1 x 3 矩陣:
| x y 1 |
這一 3 x 3 的矩陣表示一個三維點,其中 Z 坐標設置為1。 (稍后將介紹一些數學原因) 二維矩陣轉換需要在三個維度中工作。 您可以將此 1 x 3 矩陣視為表示3D 坐標系中的一個點,但始終在 Z 等於1的2D 平面上。
然后,這一 3 x 3 的矩陣與變換矩陣相乘,結果是在畫布上呈現的點:
| 1 0 0 | | x y 1 | × | 0 1 0 | = | x' y' z' | | 0 0 1 |
使用標准矩陣乘法,轉換后的點如下所示:
x' = x
y' = y
z' = 1
這是默認轉換。
2,下面講坐標轉換
對 Translate
對象調用方法時 SKCanvas
, tx
方法的和參數將 ty
Translate
成為轉換矩陣的第三行中的前兩個單元格:
| 1 0 0 | | 0 1 0 | | tx ty 1 |
乘法現在如下所示:
| 1 0 0 | | x y 1 | × | 0 1 0 | = | x' y' z' | | tx ty 1 |
下面是轉換公式:
x' = x + tx
y' = y + ty
3,縮放
縮放系數的默認值為1。 在對 Scale
新對象調用方法時 SKCanvas
,生成的轉換矩陣包含 sx
sy
對角單元格中的和參數:
| sx 0 0 | | x y 1 | × | 0 sy 0 | = | x' y' z' | | 0 0 1 |
轉換公式如下所示:
x' = sx · x
y' = sy · y
4,將skew自由變形拉伸
在調用后,轉換矩陣 Skew
包含兩個自變量的矩陣單元中的兩個參數:
│ 1 ySkew 0 │ | x y 1 | × │ xSkew 1 0 │ = | x' y' z' | │ 0 0 1 │
轉換公式為:
x' = x + xSkew · y
y' = ySkew · x + y
5, 旋轉
對於 RotateDegrees
α角度的或的調用 RotateRadians
,轉換矩陣如下所示:
│ cos(α) sin(α) 0 │ | x y 1 | × │ –sin(α) cos(α) 0 │ = | x' y' z' | │ 0 0 1 │
下面是轉換公式:
x' = cos(α) · x - sin(α) · y
y' = sin(α) · x - cos(α) · y
當α為0度時,表示為恆等矩陣。 當α為180度時,轉換矩陣如下所示:
| –1 0 0 | | 0 –1 0 | | 0 0 1 |
180度旋轉等效於水平和垂直翻轉對象,這也是通過設置–1的縮放因子來完成的。
所有這些類型的轉換都分類為 仿射 轉換。 仿射轉換從不涉及矩陣的第三列,這將保留默認值0、0和1。 非仿射轉換一文介紹了非仿射轉換。
矩陣相乘
使用轉換矩陣的一個明顯優勢是,可通過矩陣乘法獲取復合轉換,這在 SkiaSharp 文檔中通常稱為 " 串聯"。 在中,許多與轉換相關的方法是 SKCanvas
指 "串聯" 或 "預先連接"。 這是指乘法的階數,這一點非常重要,因為矩陣乘法不能交換。
例如,該方法的文檔 Translate
指出它 "用指定的翻譯預 concats 當前矩陣",而該方法的文檔 Scale
指出它 "用指定的刻度預 concats 當前矩陣"。
這意味着由方法調用指定的轉換是左操作數 (的乘數) 並且當前的轉換矩陣是右操作數) (被乘數。
假設 Translate
調用后跟 Scale
:
canvas.Translate(tx, ty);
canvas.Scale(sx, sy);
Scale
轉換乘以 Translate
復合轉換矩陣的轉換:
| sx 0 0 | | 1 0 0 | | sx 0 0 | | 0 sy 0 | × | 0 1 0 | = | 0 sy 0 | | 0 0 1 | | tx ty 1 | | tx ty 1 |
Scale``Translate
如下所示:
canvas.Scale(sx, sy);
canvas.Translate(tx, ty);
在這種情況下,乘法的順序會反轉,縮放因子有效地應用於翻譯因素:
| 1 0 0 | | sx 0 0 | | sx 0 0 | | 0 1 0 | × | 0 sy 0 | = | 0 sy 0 | | tx ty 1 | | 0 0 1 | | tx·sx ty·sy 1 |
下面是 Scale
具有透視點的方法:
canvas.Scale(sx, sy, px, py);
這等效於以下轉換和縮放調用:
canvas.Translate(px, py);
canvas.Scale(sx, sy);
canvas.Translate(–px, –py);
這三個變換矩陣按照其在代碼中的顯示方式的相反順序進行相乘:
| 1 0 0 | | sx 0 0 | | 1 0 0 | | sx 0 0 | | 0 1 0 | × | 0 sy 0 | × | 0 1 0 | = | 0 sy 0 | | –px –py 1 | | 0 0 1 | | px py 1 | | px–px·sx py–py·sy 1 |
SKMatrix 結構
SKMatrix
結構定義了九種類型的讀/寫屬性,這些屬性 float
對應於轉換矩陣的九個單元:
│ ScaleX SkewY Persp0 │ │ SkewX ScaleY Persp1 │ │ TransX TransY Persp2 │
SKMatrix
還定義了一個名為 Values
的類型的屬性 float[]
。 此屬性可用於在順序、、、、、、、和中的一次拍攝中設置或獲取九個值 ScaleX
SkewX
TransX
SkewY
ScaleY
TransY
Persp0
Persp1
Persp2
。
Persp0
Persp1
Persp2
非仿射轉換一文中討論了、和單元。 如果這些單元格的默認值為0、0和1,則轉換將乘以坐標點,如下所示:
│ ScaleX SkewY 0 │ | x y 1 | × │ SkewX ScaleY 0 │ = | x' y' z' | │ TransX TransY 1 │
x' = ScaleX · x + SkewX · y + TransX
y' = SkewX · x + ScaleY · y + TransY
z' = 1
這是完整的二維仿射轉換。 仿射轉換保留了平行線,這意味着不會將矩形轉換為平行四邊形以外的任何內容。
SKMatrix
結構定義了若干用於創建值的靜態方法 SKMatrix
。 這些都是返回 SKMatrix
值:
- MakeTranslation
- MakeScale
MakeScale
使用透視點MakeRotation
以弧度表示的角度MakeRotation
對於具有透視點的以弧度表示的角度- MakeRotationDegrees
MakeRotationDegrees
使用透視點- MakeSkew
SKMatrix
還定義了多個連接兩個矩陣的靜態方法,這意味着將它們相乘。 這些方法的名稱分別為 Concat
、 PostConcat
和 PreConcat
,每個都有兩個版本。 這些方法沒有返回值;相反,它們 SKMatrix
通過參數引用現有值 ref
。 在下面的示例中 A
, B
R
"result" ) 的、和 (都是 SKMatrix
值。
這兩種 Concat
方法的調用方式如下:
SKMatrix.Concat(ref R, A, B); SKMatrix.Concat(ref R, ref A, ref B);
這會執行以下乘法操作:
R = B × A
其他方法只有兩個參數。 第一個參數已修改,從方法調用返回時,將包含兩個矩陣的乘積。 這兩種 PostConcat
方法的調用方式如下:
SKMatrix.PostConcat(ref A, B); SKMatrix.PostConcat(ref A, ref B);
這些調用執行以下操作:
A = A × B
這兩種 PreConcat
方法類似:
SKMatrix.PreConcat(ref A, B); SKMatrix.PreConcat(ref A, ref B);
這些調用執行以下操作:
A = B × A
這些具有所有參數的方法的版本 ref
在調用基礎實現方面稍有提高,但它可能會使閱讀你的代碼的人員感到困惑,並假設具有參數的任何內容 ref
都由方法修改。 而且,傳遞作為方法之一結果的參數通常很方便 Make
,例如:
SKMatrix result;
SKMatrix.Concat(result, SKMatrix.MakeTranslation(100, 100), SKMatrix.MakeScale(3, 3));
這將創建以下矩陣:
│ 3 0 0 │ │ 0 3 0 │ │ 100 100 1 │
這是轉換轉換后的縮放變換。 在此特定情況下, SKMatrix
結構使用名為的方法提供快捷方式 SetScaleTranslate
:
SKMatrix R = new SKMatrix(); R.SetScaleTranslate(3, 3, 100, 100);
這是可以安全使用構造函數的幾個時間之一 SKMatrix
。 SetScaleTranslate
方法設置矩陣的所有九個單元格。 將 SKMatrix
構造函數用於靜態 Rotate
和方法也是安全的 RotateDegrees
:
SKMatrix R = new SKMatrix(); SKMatrix.Rotate(ref R, radians); SKMatrix.Rotate(ref R, radians, px, py); SKMatrix.RotateDegrees(ref R, degrees); SKMatrix.RotateDegrees(ref R, degrees, px, py);
這些方法 不會 將旋轉轉換連接到現有轉換。 方法設置矩陣的所有單元格。 它們在功能上與 MakeRotation
和方法相同, MakeRotationDegrees
不同之處在於它們不會實例化 SKMatrix
該值。
假設你有一個 SKPath
要顯示的對象,但你希望它具有稍有不同的方向或不同的中心點。 可以通過調用 Transform
具有參數的的方法來修改該路徑的所有坐標 SKPath
SKMatrix
。 " 路徑轉換 " 頁演示了如何執行此操作。 PathTransform
類引用 HendecagramPath
字段中的對象,但使用其構造函數將轉換應用於該路徑:
public class PathTransformPage : ContentPage { SKPath transformedPath = HendecagramArrayPage.HendecagramPath; public PathTransformPage() { Title = "Path Transform"; SKCanvasView canvasView = new SKCanvasView(); canvasView.PaintSurface += OnCanvasViewPaintSurface; Content = canvasView; SKMatrix matrix = SKMatrix.MakeScale(3, 3); SKMatrix.PostConcat(ref matrix, SKMatrix.MakeRotationDegrees(360f / 22)); SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(300, 300)); transformedPath.Transform(matrix); } ... }
HendecagramPath
對象的中心位於 (0,0) ,星號的11個點從該中心向外延伸,方向為100個單位。 這意味着路徑具有正坐標和負坐標。 路徑轉換 頁面首選使用星形三次,並具有所有正坐標。 此外,它也不希望星形上的一個點直接指向。 它需要改用星號的一個點來向下直接指向。 (因為星形包含11個點,所以不能同時具有這兩個點。 ) 這需要將星形旋轉360度除以22。
構造函數 SKMatrix
使用方法從三個單獨的轉換生成一個對象 PostConcat
,該方法采用以下模式,其中 A、B 和 C 是的實例 SKMatrix
:
SKMatrix matrix = A;
SKMatrix.PostConcat(ref A, B); SKMatrix.PostConcat(ref A, C);
這是一系列連續的祖,因此結果如下:
A × B × C
連續的祖有助於了解每個轉換的用途。 縮放轉換會將路徑坐標的大小增加3倍,因此,坐標范圍為–300到300。 旋轉變換圍繞其原點旋轉星形。 然后,轉換轉換向右和向下移動了300像素,因此所有坐標都變為正值。
還有其他序列產生同一矩陣。 下面是另一種:
SKMatrix matrix = SKMatrix.MakeRotationDegrees(360f / 22); SKMatrix.PostConcat(ref matrix, SKMatrix.MakeTranslation(100, 100)); SKMatrix.PostConcat(ref matrix, SKMatrix.MakeScale(3, 3));
這會先圍繞中心旋轉路徑,然后將其向右和向下平移100像素,以使所有坐標為正值。 然后,該星形相對於其新的左上角的大小增加,這是 (0,0) 的點。
PaintSurface
處理程序可以簡單地呈現以下路徑:
public class PathTransformPage : ContentPage { ... void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); using (SKPaint paint = new SKPaint()) { paint.Style = SKPaintStyle.Stroke; paint.Color = SKColors.Magenta; paint.StrokeWidth = 5; canvas.DrawPath(transformedPath, paint); } } }
它顯示在畫布的左上角:
此程序的構造函數將矩陣應用到具有以下調用的路徑:
transformedPath.Transform(matrix);
該路徑 不 會將此矩陣保留為屬性。 相反,它會將轉換應用於路徑的所有坐標。 如果 Transform
再次調用,將再次應用該轉換,而您可以返回的唯一方法是應用另一個撤消轉換的矩陣。 幸運的是,該 SKMatrix
結構定義了一個 TryInvert
方法,該方法可獲取反向給定矩陣的矩陣:
SKMatrix inverse;
bool success = matrix.TryInverse(out inverse);
將調用方法 TryInverse
,因為並非所有矩陣都是可逆的,但是不可能將不可逆矩陣用於圖形轉換。
您還可以將矩陣轉換應用於 SKPoint
值、點數組、 SKRect
甚至只是程序中的單個數字。 此 SKMatrix
結構使用以單詞開頭的方法集合(如下所示)來支持這些操作 Map
:
SKPoint transformedPoint = matrix.MapPoint(point);
SKPoint transformedPoint = matrix.MapPoint(x, y);
SKPoint[] transformedPoints = matrix.MapPoints(pointArray);
float transformedValue = matrix.MapRadius(floatValue); SKRect transformedRect = matrix.MapRect(rect);
如果使用最后一種方法,請記住, SKRect
結構不能表示旋轉矩形。 此方法僅適用於 SKMatrix
表示轉換和縮放的值。
交互式試驗
獲得仿射轉換的一種方法是在屏幕上以交互方式移動位圖的三個角並查看轉換結果。 這是 " 顯示仿射矩陣 " 頁面背后的概念。 此頁需要另外兩個也用於其他演示的類:
TouchPoint
類顯示可在屏幕上拖動的半透明圓圈。 TouchPoint
要求作為的 SKCanvasView
父級的或元素 SKCanvasView
具有 TouchEffect
附加的。 將 Capture
屬性設置為 true
。 在 TouchAction
事件處理程序中,程序必須 ProcessTouchEvent
TouchPoint
為每個實例調用中的方法 TouchPoint
。 true
如果觸摸事件導致觸摸點移動,則方法返回。 而且, PaintSurface
處理程序必須 Paint
在每個實例中調用方法 TouchPoint
,並向其傳遞 SKCanvas
對象。
TouchPoint
演示 SkiaSharp 視覺對象可以在單獨的類中封裝的常見方法。 類可定義用於指定視覺對象特性的屬性,並且具有參數的名為 Paint
的方法 SKCanvas
可以呈現視覺對象。
的 Center
屬性 TouchPoint
指示對象的位置。 此屬性可設置為初始化位置;當用戶在畫布上拖動圓圈時,屬性將發生變化。
" 顯示仿射矩陣" 頁 還需要 MatrixDisplay
類。 此類顯示對象的單元格 SKMatrix
。 它有兩個公共方法: Measure
若要獲取呈現的矩陣的尺寸,並 Paint
顯示它。 類包含類型為的 MatrixPaint
屬性 SKPaint
,可將其替換為不同的字號或顏色。
ShowAffineMatrixPage文件實例化 SKCanvasView
並附加一個 TouchEffect
。 ShowAffineMatrixPage.xaml.cs代碼隱藏文件創建三個 TouchPoint
對象,然后將其設置為與從嵌入資源加載的位圖的三個角相對應的位置:
public partial class ShowAffineMatrixPage : ContentPage { SKMatrix matrix; SKBitmap bitmap; SKSize bitmapSize; TouchPoint[] touchPoints = new TouchPoint[3]; MatrixDisplay matrixDisplay = new MatrixDisplay(); public ShowAffineMatrixPage() { InitializeComponent(); string resourceID = "SkiaSharpFormsDemos.Media.SeatedMonkey.jpg"; Assembly assembly = GetType().GetTypeInfo().Assembly; using (Stream stream = assembly.GetManifestResourceStream(resourceID)) { bitmap = SKBitmap.Decode(stream); } touchPoints[0] = new TouchPoint(100, 100); // upper-left corner touchPoints[1] = new TouchPoint(bitmap.Width + 100, 100); // upper-right corner touchPoints[2] = new TouchPoint(100, bitmap.Height + 100); // lower-left corner bitmapSize = new SKSize(bitmap.Width, bitmap.Height); matrix = ComputeMatrix(bitmapSize, touchPoints[0].Center, touchPoints[1].Center, touchPoints[2].Center); } ... }
仿射矩陣由三個點唯一定義。 這三個 TouchPoint
對象對應於位圖的左上角、右上角和左下角。 由於仿射矩陣只能將矩形轉換為平行四邊形,因此第四個點將被另三個點所隱含。 構造函數通過調用來結束 ComputeMatrix
,這 SKMatrix
三個點計算對象的單元格。
TouchAction
處理程序調用 ProcessTouchEvent
每個的方法 TouchPoint
。 scale
值從坐標轉換 Xamarin.Forms 為像素:
public partial class ShowAffineMatrixPage : ContentPage { ... void OnTouchEffectAction(object sender, TouchActionEventArgs args) { bool touchPointMoved = false; foreach (TouchPoint touchPoint in touchPoints) { float scale = canvasView.CanvasSize.Width / (float)canvasView.Width; SKPoint point = new SKPoint(scale * (float)args.Location.X, scale * (float)args.Location.Y); touchPointMoved |= touchPoint.ProcessTouchEvent(args.Id, args.Type, point); } if (touchPointMoved) { matrix = ComputeMatrix(bitmapSize, touchPoints[0].Center, touchPoints[1].Center, touchPoints[2].Center); canvasView.InvalidateSurface(); } } ... }
如果任何 TouchPoint
已移動,則方法將 ComputeMatrix
再次調用並使圖面無效。
ComputeMatrix
方法確定這三個點隱含的矩陣。 "矩陣" 是指 A
基於三個點將一個象素方形矩形轉換為平行四邊形,而 "縮放" 轉換稱為 "將 S
位圖縮放為一個像素的矩形"。 復合矩陣為 S
× A
:
public partial class ShowAffineMatrixPage : ContentPage { ... static SKMatrix ComputeMatrix(SKSize size, SKPoint ptUL, SKPoint ptUR, SKPoint ptLL) { // Scale transform SKMatrix S = SKMatrix.MakeScale(1 / size.Width, 1 / size.Height); // Affine transform SKMatrix A = new SKMatrix { ScaleX = ptUR.X - ptUL.X, SkewY = ptUR.Y - ptUL.Y, SkewX = ptLL.X - ptUL.X, ScaleY = ptLL.Y - ptUL.Y, TransX = ptUL.X, TransY = ptUL.Y, Persp2 = 1 }; SKMatrix result = SKMatrix.MakeIdentity(); SKMatrix.Concat(ref result, A, S); return result; } ... }
最后,該 PaintSurface
方法基於該矩陣呈現位圖,在屏幕底部顯示矩陣,並在位圖的三個角呈現觸摸點:
public partial class ShowAffineMatrixPage : ContentPage { ... void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args) { SKImageInfo info = args.Info; SKSurface surface = args.Surface; SKCanvas canvas = surface.Canvas; canvas.Clear(); // Display the bitmap using the matrix canvas.Save(); canvas.SetMatrix(matrix); canvas.DrawBitmap(bitmap, 0, 0); canvas.Restore(); // Display the matrix in the lower-right corner SKSize matrixSize = matrixDisplay.Measure(matrix); matrixDisplay.Paint(canvas, matrix, new SKPoint(info.Width - matrixSize.Width, info.Height - matrixSize.Height)); // Display the touchpoints foreach (TouchPoint touchPoint in touchPoints) { touchPoint.Paint(canvas); } } }
下面的 iOS 屏幕顯示第一次加載頁面時的位圖,而另兩個屏幕在執行某種操作后顯示該位圖:
[](matrix-images/showaffinematrix-large.png#lightbox ""顯示仿射矩陣" 頁的三個屏幕截圖")
盡管觸摸點看起來好像是觸摸點,但這只是一種錯覺。 從觸摸點計算的矩陣會轉換位圖,使拐角與觸摸點重合。
用戶對位圖進行移動、調整大小和旋轉,並不是通過拖動角來實現,但通過直接在對象上使用一個或兩個手指進行拖動、縮小和旋轉,這種方式更加自然。 下一文 觸摸操作中對此進行了介紹。
3 x 3 矩陣的原因
可能需要二維圖形系統只需要 2 x 2 的轉換矩陣:
│ ScaleX SkewY │ | x y | × │ │ = | x' y' | │ SkewX ScaleY │
這適用於縮放、旋轉甚至扭曲,但它不能是轉換的最基本轉換。
問題在於,2 x 2 的矩陣表示兩個維度中的 線性 轉換。 線性轉換保留一些基本算術運算,但其中一個含義是線性轉換永遠不會改變點 (0,0) 。 線性轉換無法進行轉換。
在三個維度中,線性變換矩陣如下所示:
│ ScaleX SkewYX SkewZX │ | x y z | × │ SkewXY ScaleY SkewZY │ = | x' y' z' | │ SkewXZ SkewYZ ScaleZ │
標有標簽的單元格 SkewXY
表示該值基於 Y 的值傾斜 x 坐標; 單元 SkewXZ
表示該值基於 Z 的值來傾斜 x 坐標; 對於其他單元格,值會傾斜 Skew
。
可以通過將和設置為0,將此3D 變換矩陣限制為二維平面 SkewZX
SkewZY
,並將設置 ScaleZ
為1:
│ ScaleX SkewYX 0 │ | x y z | × │ SkewXY ScaleY 0 │ = | x' y' z' | │ SkewXZ SkewYZ 1 │
如果二維圖形完全在3D 空間(其中 Z 等於1)上繪制,則轉換乘法如下所示:
│ ScaleX SkewYX 0 │ | x y 1 | × │ SkewXY ScaleY 0 │ = | x' y' 1 | │ SkewXZ SkewYZ 1 │
所有內容保持在二維平面上,其中 Z 等於1,但 SkewXZ
和 SkewYZ
單元格實際上變成了二維轉換因素。
這就是三維線性轉換如何作為二維非線性轉換的方式。 按照類比 (,3D 圖形中的變換基於4個 4 x 4 的矩陣。 )
SKMatrix
SkiaSharp 中的結構定義該第三行的屬性:
│ ScaleX SkewY Persp0 │ | x y 1 | × │ SkewX ScaleY Persp1 │ = | x' y' z` | │ TransX TransY Persp2 │
和的非零值 Persp0
會 Persp1
導致在 Z 等於1的情況下將對象移出二維平面的變換。 在 非仿射轉換一文中介紹了將這些對象移回該平面時會發生什么情況。
相關鏈接
對應3d繪制,需要4維矩陣:SkM44
坐標體系:左上角開始,往右下走:
* Skia assumes a right-handed coordinate system:
* +X goes to the right
* +Y goes down
* +Z goes into the screen (away from the viewer)
初始化用二維或者1維數組(按行優先RowMajor,列優先方式ColMajor)
默認時列優先模式存儲:
/* Stored in column-major. * Indices * 0 4 8 12 1 0 0 trans_x * 1 5 9 13 e.g. 0 1 0 trans_y * 2 6 10 14 0 0 1 trans_z * 3 7 11 15 0 0 0 1 */
平移:坐標在列上
SkM44& setTranslate(SkScalar x, SkScalar y, SkScalar z = 0) { *this = { 1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1 }; return *this; } SkM44& setScale(SkScalar x, SkScalar y, SkScalar z = 1) { *this = { x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1 }; return *this; }
x不等於0時做拉伸:
/** * A matrix is categorized as 'perspective' if the bottom row is not [0, 0, 0, 1]. * For most uses, a bottom row of [0, 0, 0, X] behaves like a non-perspective matrix, though * it will be categorized as perspective. Calling normalizePerspective() will change the * matrix such that, if its bottom row was [0, 0, 0, X], it will be changed to [0, 0, 0, 1] * by scaling the rest of the matrix by 1/X. * * | A B C D | | A/X B/X C/X D/X | * | E F G H | -> | E/X F/X G/X H/X | for X != 0 * | I J K L | | I/X J/X K/X L/X | * | 0 0 0 X | | 0 0 0 1 | */ void normalizePerspective();
與2d的相互轉換,去掉了第三行,第三列:
/* When converting from SkM44 to SkMatrix, the third row and * column is dropped. When converting from SkMatrix to SkM44 * the third row and column remain as identity: * [ a b c ] [ a b 0 c ] * [ d e f ] -> [ d e 0 f ] * [ g h i ] [ 0 0 1 0 ] * [ g h 0 i ] */