blob: c9bb1ff475da26b0ab88d069a68f94a9d1821a8d [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/flow/matrix_decomposition.h"
namespace flutter {
static inline SkV3 SkV3Combine(const SkV3& a,
float a_scale,
const SkV3& b,
float b_scale) {
return (a * a_scale) + (b * b_scale);
}
MatrixDecomposition::MatrixDecomposition(const SkMatrix& matrix)
: MatrixDecomposition(SkM44{matrix}) {}
// Use custom normalize to avoid skia precision loss/normalize() privatization.
static inline void SkV3Normalize(SkV3& v) {
double mag = sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
double scale = 1.0 / mag;
v.x *= scale;
v.y *= scale;
v.z *= scale;
}
MatrixDecomposition::MatrixDecomposition(SkM44 matrix) : valid_(false) {
if (matrix.rc(3, 3) == 0) {
return;
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
matrix.setRC(j, i, matrix.rc(j, i) / matrix.rc(3, 3));
}
}
SkM44 perpective_matrix = matrix;
for (int i = 0; i < 3; i++) {
perpective_matrix.setRC(3, i, 0.0);
}
perpective_matrix.setRC(3, 3, 1.0);
SkM44 inverted(SkM44::Uninitialized_Constructor::kUninitialized_Constructor);
if (!perpective_matrix.invert(&inverted)) {
return;
}
if (matrix.rc(3, 0) != 0.0 || matrix.rc(3, 1) != 0.0 ||
matrix.rc(3, 2) != 0.0) {
const SkV4 right_hand_side = matrix.row(3);
perspective_ = inverted.transpose() * right_hand_side;
matrix.setRow(3, {0, 0, 0, 1});
}
translation_ = {matrix.rc(0, 3), matrix.rc(1, 3), matrix.rc(2, 3)};
matrix.setRC(0, 3, 0.0);
matrix.setRC(1, 3, 0.0);
matrix.setRC(2, 3, 0.0);
SkV3 row[3];
for (int i = 0; i < 3; i++) {
row[i] = {matrix.rc(0, i), matrix.rc(1, i), matrix.rc(2, i)};
}
scale_.x = row[0].length();
SkV3Normalize(row[0]);
shear_.x = row[0].dot(row[1]);
row[1] = SkV3Combine(row[1], 1.0, row[0], -shear_.x);
scale_.y = row[1].length();
SkV3Normalize(row[1]);
shear_.x /= scale_.y;
shear_.y = row[0].dot(row[2]);
row[2] = SkV3Combine(row[2], 1.0, row[0], -shear_.y);
shear_.z = row[1].dot(row[2]);
row[2] = SkV3Combine(row[2], 1.0, row[1], -shear_.z);
scale_.z = row[2].length();
SkV3Normalize(row[2]);
shear_.y /= scale_.z;
shear_.z /= scale_.z;
if (row[0].dot(row[1].cross(row[2])) < 0) {
scale_ *= -1;
for (int i = 0; i < 3; i++) {
row[i] *= -1;
}
}
rotation_.x = 0.5 * sqrt(fmax(1.0 + row[0].x - row[1].y - row[2].z, 0.0));
rotation_.y = 0.5 * sqrt(fmax(1.0 - row[0].x + row[1].y - row[2].z, 0.0));
rotation_.z = 0.5 * sqrt(fmax(1.0 - row[0].x - row[1].y + row[2].z, 0.0));
rotation_.w = 0.5 * sqrt(fmax(1.0 + row[0].x + row[1].y + row[2].z, 0.0));
if (row[2].y > row[1].z) {
rotation_.x = -rotation_.x;
}
if (row[0].z > row[2].x) {
rotation_.y = -rotation_.y;
}
if (row[1].x > row[0].y) {
rotation_.z = -rotation_.z;
}
valid_ = true;
}
MatrixDecomposition::~MatrixDecomposition() = default;
bool MatrixDecomposition::IsValid() const {
return valid_;
}
} // namespace flutter