1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/flow/matrix_decomposition.h"
6
7namespace flutter {
8
9static inline SkV3 SkV3Combine(const SkV3& a,
10 float a_scale,
11 const SkV3& b,
12 float b_scale) {
13 return (a * a_scale) + (b * b_scale);
14}
15
16MatrixDecomposition::MatrixDecomposition(const SkMatrix& matrix)
17 : MatrixDecomposition(SkM44{matrix}) {}
18
19// Use custom normalize to avoid skia precision loss/normalize() privatization.
20static inline void SkV3Normalize(SkV3* v) {
21 double mag = sqrt(v->x * v->x + v->y * v->y + v->z * v->z);
22 double scale = 1.0 / mag;
23 v->x *= scale;
24 v->y *= scale;
25 v->z *= scale;
26}
27
28MatrixDecomposition::MatrixDecomposition(SkM44 matrix) : valid_(false) {
29 if (matrix.rc(3, 3) == 0) {
30 return;
31 }
32
33 for (int i = 0; i < 4; i++) {
34 for (int j = 0; j < 4; j++) {
35 matrix.setRC(j, i, matrix.rc(j, i) / matrix.rc(3, 3));
36 }
37 }
38
39 SkM44 perpective_matrix = matrix;
40 for (int i = 0; i < 3; i++) {
41 perpective_matrix.setRC(3, i, 0.0);
42 }
43
44 perpective_matrix.setRC(3, 3, 1.0);
45
46 SkM44 inverted(SkM44::Uninitialized_Constructor::kUninitialized_Constructor);
47 if (!perpective_matrix.invert(&inverted)) {
48 return;
49 }
50
51 if (matrix.rc(3, 0) != 0.0 || matrix.rc(3, 1) != 0.0 ||
52 matrix.rc(3, 2) != 0.0) {
53 const SkV4 right_hand_side = matrix.row(3);
54
55 perspective_ = inverted.transpose() * right_hand_side;
56
57 matrix.setRow(3, {0, 0, 0, 1});
58 }
59
60 translation_ = {matrix.rc(0, 3), matrix.rc(1, 3), matrix.rc(2, 3)};
61
62 matrix.setRC(0, 3, 0.0);
63 matrix.setRC(1, 3, 0.0);
64 matrix.setRC(2, 3, 0.0);
65
66 SkV3 row[3];
67 for (int i = 0; i < 3; i++) {
68 row[i] = {matrix.rc(0, i), matrix.rc(1, i), matrix.rc(2, i)};
69 }
70
71 scale_.x = row[0].length();
72
73 SkV3Normalize(&row[0]);
74
75 shear_.x = row[0].dot(row[1]);
76 row[1] = SkV3Combine(row[1], 1.0, row[0], -shear_.x);
77
78 scale_.y = row[1].length();
79
80 SkV3Normalize(&row[1]);
81
82 shear_.x /= scale_.y;
83
84 shear_.y = row[0].dot(row[2]);
85 row[2] = SkV3Combine(row[2], 1.0, row[0], -shear_.y);
86 shear_.z = row[1].dot(row[2]);
87 row[2] = SkV3Combine(row[2], 1.0, row[1], -shear_.z);
88
89 scale_.z = row[2].length();
90
91 SkV3Normalize(&row[2]);
92
93 shear_.y /= scale_.z;
94 shear_.z /= scale_.z;
95
96 if (row[0].dot(row[1].cross(row[2])) < 0) {
97 scale_ *= -1;
98
99 for (int i = 0; i < 3; i++) {
100 row[i] *= -1;
101 }
102 }
103
104 rotation_.x = 0.5 * sqrt(fmax(1.0 + row[0].x - row[1].y - row[2].z, 0.0));
105 rotation_.y = 0.5 * sqrt(fmax(1.0 - row[0].x + row[1].y - row[2].z, 0.0));
106 rotation_.z = 0.5 * sqrt(fmax(1.0 - row[0].x - row[1].y + row[2].z, 0.0));
107 rotation_.w = 0.5 * sqrt(fmax(1.0 + row[0].x + row[1].y + row[2].z, 0.0));
108
109 if (row[2].y > row[1].z) {
110 rotation_.x = -rotation_.x;
111 }
112 if (row[0].z > row[2].x) {
113 rotation_.y = -rotation_.y;
114 }
115 if (row[1].x > row[0].y) {
116 rotation_.z = -rotation_.z;
117 }
118
119 valid_ = true;
120}
121
122MatrixDecomposition::~MatrixDecomposition() = default;
123
124bool MatrixDecomposition::IsValid() const {
125 return valid_;
126}
127
128} // namespace flutter
129