1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/354829279): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#ifndef UI_GFX_GEOMETRY_MATRIX44_H_
#define UI_GFX_GEOMETRY_MATRIX44_H_
#include <optional>
#include "base/check_op.h"
#include "base/component_export.h"
#include "base/containers/span.h"
#include "ui/gfx/geometry/double4.h"
namespace gfx {
struct DecomposedTransform;
// This is the underlying data structure of Transform. Don't use this type
// directly.
//
// Throughout this class, we will be speaking in column vector convention.
// i.e. Applying a transform T to vector V is T * V.
// The components of the matrix and the vector look like:
// \ col
// r \ 0 1 2 3
// o 0 | scale_x skew_xy skew_xz trans_x | | x |
// w 1 | skew_yx scale_y skew_yz trans_y | * | y |
// 2 | skew_zx skew_zy scale_z trans_z | | z |
// 3 | persp_x persp_y persp_z persp_w | | w |
//
// Note that the names are just for remembering and don't have the exact
// meanings when other components exist.
//
// The components correspond to the DOMMatrix mij (i,j = 1..4) components:
// i = col + 1
// j = row + 1
class COMPONENT_EXPORT(GEOMETRY_SKIA) Matrix44 {
public:
enum UninitializedTag { kUninitialized };
explicit Matrix44(UninitializedTag) {}
constexpr Matrix44()
: matrix_{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}} {}
// The parameters are in col-major order.
// clang-format off
constexpr Matrix44(double r0c0, double r1c0, double r2c0, double r3c0,
double r0c1, double r1c1, double r2c1, double r3c1,
double r0c2, double r1c2, double r2c2, double r3c2,
double r0c3, double r1c3, double r2c3, double r3c3)
// matrix_ is indexed by [col][row] (i.e. col-major).
: matrix_{{r0c0, r1c0, r2c0, r3c0},
{r0c1, r1c1, r2c1, r3c1},
{r0c2, r1c2, r2c2, r3c2},
{r0c3, r1c3, r2c3, r3c3}} {}
// clang-format on
bool operator==(const Matrix44& other) const {
return AllTrue(Col(0) == other.Col(0)) && AllTrue(Col(1) == other.Col(1)) &&
AllTrue(Col(2) == other.Col(2)) && AllTrue(Col(3) == other.Col(3));
}
// Returns true if the matrix is identity.
bool IsIdentity() const { return *this == Matrix44(); }
// Returns true if the matrix contains translate or is identity.
bool IsIdentityOrTranslation() const {
return AllTrue(Col(0) == Double4{1, 0, 0, 0}) &&
AllTrue(Col(1) == Double4{0, 1, 0, 0}) &&
AllTrue(Col(2) == Double4{0, 0, 1, 0}) && matrix_[3][3] == 1;
}
// Returns true if the matrix only contains scale or translate or is identity.
bool IsScaleOrTranslation() const {
return AllTrue(Double4{matrix_[0][1], matrix_[0][2], matrix_[0][3],
matrix_[1][0]} == Double4{0, 0, 0, 0}) &&
AllTrue(Double4{matrix_[1][2], matrix_[1][3], matrix_[2][0],
matrix_[2][1]} == Double4{0, 0, 0, 0}) &&
matrix_[2][3] == 0 && matrix_[3][3] == 1;
}
// Returns true if the matrix only contains scale or is identity.
bool IsScale() const {
return IsScaleOrTranslation() && AllTrue(Col(3) == Double4{0, 0, 0, 1});
}
bool IsFlat() const {
return AllTrue(Col(2) == Double4{0, 0, 1, 0}) &&
AllTrue(Double4{matrix_[0][2], matrix_[1][2], 0, matrix_[3][2]} ==
Double4{0, 0, 0, 0});
}
bool HasPerspective() const {
return !AllTrue(Double4{matrix_[0][3], matrix_[1][3], matrix_[2][3],
matrix_[3][3]} == Double4{0, 0, 0, 1});
}
bool Is2dTransform() const { return IsFlat() && !HasPerspective(); }
// Gets a value at |row|, |col| from the matrix.
constexpr double rc(int row, int col) const {
DCHECK_LE(static_cast<unsigned>(row), 3u);
DCHECK_LE(static_cast<unsigned>(col), 3u);
return matrix_[col][row];
}
// Set a value in the matrix at |row|, |col|.
void set_rc(int row, int col, double value) {
DCHECK_LE(static_cast<unsigned>(row), 3u);
DCHECK_LE(static_cast<unsigned>(col), 3u);
matrix_[col][row] = value;
}
void GetColMajor(double[16]) const;
void GetColMajorF(float[16]) const;
// this = this * translation.
void PreTranslate(double dx, double dy);
void PreTranslate3d(double dx, double dy, double dz);
// this = translation * this.
void PostTranslate(double dx, double dy);
void PostTranslate3d(double dx, double dy, double dz);
// this = this * scale.
void PreScale(double sx, double sy);
void PreScale3d(double sx, double sy, double sz);
// this = scale * this.
void PostScale(double sx, double sy);
void PostScale3d(double sx, double sy, double sz);
// Rotates this matrix about the specified unit-length axis vector,
// by an angle specified by its sin() and cos(). This does not attempt to
// verify that axis(x, y, z).length() == 1 or that the sin, cos values are
// correct. this = this * rotation.
void RotateUnitSinCos(double x,
double y,
double z,
double sin_angle,
double cos_angle);
// Special case for x, y or z axis of the above function.
void RotateAboutXAxisSinCos(double sin_angle, double cos_angle);
void RotateAboutYAxisSinCos(double sin_angle, double cos_angle);
void RotateAboutZAxisSinCos(double sin_angle, double cos_angle);
// this = this * skew.
void Skew(double tan_skew_x, double tan_skew_y);
// |1 skew[0] skew[1] 0|
// this = this * |0 1 skew[2] 0|
// |0 0 1 0|
// |0 0 0 1|
void ApplyDecomposedSkews(base::span<const double, 3> skews);
// this = this * perspective.
void ApplyPerspectiveDepth(double perspective);
// this = this * m.
void PreConcat(const Matrix44& m) { SetConcat(*this, m); }
// this = m * this.
void PostConcat(const Matrix44& m) { SetConcat(m, *this); }
// this = a * b.
void SetConcat(const Matrix44& a, const Matrix44& b);
// Returns true and set |inverse| to the inverted matrix if this matrix
// is invertible. Otherwise return false and leave the |inverse| parameter
// unchanged.
bool GetInverse(Matrix44& inverse) const;
bool IsInvertible() const;
double Determinant() const;
// Transposes this matrix in place.
void Transpose();
// See Transform::Zoom().
void Zoom(double zoom_factor);
// Applies the matrix to the vector in place.
void MapVector4(double vec[4]) const;
// Same as above, but assumes the vec[2] is 0 and vec[3] is 1, discards
// vec[2], and returns vec[3].
double MapVector2(double vec[2]) const;
void Flatten();
std::optional<DecomposedTransform> Decompose() const;
private:
std::optional<DecomposedTransform> Decompose2d() const;
ALWAYS_INLINE Double4 Col(int i) const { return LoadDouble4(matrix_[i]); }
ALWAYS_INLINE void SetCol(int i, Double4 v) { StoreDouble4(v, matrix_[i]); }
// This is indexed by [col][row].
double matrix_[4][4];
};
} // namespace gfx
#endif // UI_GFX_GEOMETRY_MATRIX44_H_
|