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 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
|
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GFX_GEOMETRY_MATRIX44_H_
#define UI_GFX_GEOMETRY_MATRIX44_H_
#include <atomic>
#include <cstring>
#include "build/build_config.h"
#include "third_party/skia/include/core/SkMatrix.h"
#include "ui/gfx/geometry/geometry_skia_export.h"
#if BUILDFLAG(IS_MAC)
struct CATransform3D;
#endif
namespace gfx {
struct Vector4 {
SkScalar fData[4];
Vector4() { this->set(0, 0, 0, 1); }
Vector4(const Vector4& src) { memcpy(fData, src.fData, sizeof(fData)); }
Vector4(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
fData[0] = x;
fData[1] = y;
fData[2] = z;
fData[3] = w;
}
Vector4& operator=(const Vector4& src) {
memcpy(fData, src.fData, sizeof(fData));
return *this;
}
bool operator==(const Vector4& v) const {
return fData[0] == v.fData[0] && fData[1] == v.fData[1] &&
fData[2] == v.fData[2] && fData[3] == v.fData[3];
}
bool operator!=(const Vector4& v) const { return !(*this == v); }
bool equals(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
return fData[0] == x && fData[1] == y && fData[2] == z && fData[3] == w;
}
void set(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) {
fData[0] = x;
fData[1] = y;
fData[2] = z;
fData[3] = w;
}
};
// This is the underlying data structure of Transform. Don't use this type
// directly. The public methods can be called through Transform::matrix().
class GEOMETRY_SKIA_EXPORT Matrix44 {
public:
enum Uninitialized_Constructor { kUninitialized_Constructor };
explicit Matrix44(Uninitialized_Constructor) {}
constexpr Matrix44()
: fMat{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}},
fTypeMask(kIdentity_Mask) {}
// The parameters are in row-major order.
Matrix44(SkScalar col1row1,
SkScalar col2row1,
SkScalar col3row1,
SkScalar col4row1,
SkScalar col1row2,
SkScalar col2row2,
SkScalar col3row2,
SkScalar col4row2,
SkScalar col1row3,
SkScalar col2row3,
SkScalar col3row3,
SkScalar col4row3,
SkScalar col1row4,
SkScalar col2row4,
SkScalar col3row4,
SkScalar col4row4)
// fMat is indexed by [col][row] (i.e. col-major).
: fMat{{col1row1, col1row2, col1row3, col1row4},
{col2row1, col2row2, col2row3, col2row4},
{col3row1, col3row2, col3row3, col3row4},
{col4row1, col4row2, col4row3, col4row4}} {
recomputeTypeMask();
}
Matrix44(const Matrix44& a, const Matrix44& b) { this->setConcat(a, b); }
bool operator==(const Matrix44& other) const;
bool operator!=(const Matrix44& other) const { return !(other == *this); }
/* When converting from Matrix44 to SkMatrix, the third row and
* column is dropped. When converting from SkMatrix to Matrix44
* 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 ]
*/
explicit Matrix44(const SkMatrix&);
// Inverse conversion of the above.
SkMatrix asM33() const;
using TypeMask = uint8_t;
enum : TypeMask {
kIdentity_Mask = 0,
kTranslate_Mask = 1 << 0, //!< set if the matrix has translation
kScale_Mask = 1 << 1, //!< set if the matrix has any scale != 1
kAffine_Mask = 1 << 2, //!< set if the matrix skews or rotates
kPerspective_Mask = 1 << 3, //!< set if the matrix is in perspective
};
/**
* Returns a bitfield describing the transformations the matrix may
* perform. The bitfield is computed conservatively, so it may include
* false positives. For example, when kPerspective_Mask is true, all
* other bits may be set to true even in the case of a pure perspective
* transform.
*/
inline TypeMask getType() const { return fTypeMask; }
/**
* Return true if the matrix is identity.
*/
inline bool isIdentity() const { return kIdentity_Mask == this->getType(); }
/**
* Return true if the matrix contains translate or is identity.
*/
inline bool isTranslate() const {
return !(this->getType() & ~kTranslate_Mask);
}
/**
* Return true if the matrix only contains scale or translate or is identity.
*/
inline bool isScaleTranslate() const {
return !(this->getType() & ~(kScale_Mask | kTranslate_Mask));
}
/**
* Returns true if the matrix only contains scale or is identity.
*/
inline bool isScale() const { return !(this->getType() & ~kScale_Mask); }
inline bool hasPerspective() const {
return SkToBool(this->getType() & kPerspective_Mask);
}
void setIdentity();
/**
* get a value from the matrix. The row,col parameters work as follows:
* (0, 0) scale-x
* (0, 3) translate-x
* (3, 0) perspective-x
*/
inline SkScalar rc(int row, int col) const {
SkASSERT((unsigned)row <= 3);
SkASSERT((unsigned)col <= 3);
return fMat[col][row];
}
/**
* set a value in the matrix. The row,col parameters work as follows:
* (0, 0) scale-x
* (0, 3) translate-x
* (3, 0) perspective-x
*/
inline void setRC(int row, int col, SkScalar value) {
SkASSERT((unsigned)row <= 3);
SkASSERT((unsigned)col <= 3);
fMat[col][row] = value;
this->recomputeTypeMask();
}
/** These methods allow one to efficiently read matrix entries into an
* array. The given array must have room for exactly 16 entries. Whenever
* possible, they will try to use memcpy rather than an entry-by-entry
* copy.
*
* Col major indicates that consecutive elements of columns will be stored
* contiguously in memory. Row major indicates that consecutive elements
* of rows will be stored contiguously in memory.
*/
void getColMajor(float[]) const;
void getRowMajor(float[]) const;
/** These methods allow one to efficiently set all matrix entries from an
* array. The given array must have room for exactly 16 entries. Whenever
* possible, they will try to use memcpy rather than an entry-by-entry
* copy.
*
* Col major indicates that input memory will be treated as if consecutive
* elements of columns are stored contiguously in memory. Row major
* indicates that input memory will be treated as if consecutive elements
* of rows are stored contiguously in memory.
*/
void setColMajor(const float[]);
void setRowMajor(const float[]);
#if BUILDFLAG(IS_MAC)
CATransform3D ToCATransform3D() const;
#endif
Matrix44& setTranslate(SkScalar dx, SkScalar dy, SkScalar dz);
Matrix44& preTranslate(SkScalar dx, SkScalar dy, SkScalar dz);
Matrix44& postTranslate(SkScalar dx, SkScalar dy, SkScalar dz);
Matrix44& setScale(SkScalar sx, SkScalar sy, SkScalar sz);
Matrix44& preScale(SkScalar sx, SkScalar sy, SkScalar sz);
Matrix44& postScale(SkScalar sx, SkScalar sy, SkScalar sz);
inline Matrix44& setScale(SkScalar scale) {
return this->setScale(scale, scale, scale);
}
inline Matrix44& preScale(SkScalar scale) {
return this->preScale(scale, scale, scale);
}
inline Matrix44& postScale(SkScalar scale) {
return this->postScale(scale, scale, scale);
}
// Sets this matrix to rotate 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.
void setRotateUnitSinCos(SkScalar x,
SkScalar y,
SkScalar z,
SkScalar sin_angle,
SkScalar cos_angle);
// Special case for x, y or z axis of the above function.
void setRotateAboutXAxisSinCos(SkScalar sin_angle, SkScalar cos_angle);
void setRotateAboutYAxisSinCos(SkScalar sin_angle, SkScalar cos_angle);
void setRotateAboutZAxisSinCos(SkScalar sin_angle, SkScalar cos_angle);
void setConcat(const Matrix44& a, const Matrix44& b);
inline void preConcat(const Matrix44& m) { this->setConcat(*this, m); }
inline void postConcat(const Matrix44& m) { this->setConcat(m, *this); }
friend Matrix44 operator*(const Matrix44& a, const Matrix44& b) {
return Matrix44(a, b);
}
/** If this is invertible, return that in inverse and return true. If it is
not invertible, return false and leave the inverse parameter in an
unspecified state.
*/
bool invert(Matrix44* inverse) const;
/** Transpose this matrix in place. */
void transpose();
/** Apply the matrix to the src vector, returning the new vector in dst.
It is legal for src and dst to point to the same memory.
*/
void mapScalars(const SkScalar src[4], SkScalar dst[4]) const;
inline void mapScalars(SkScalar vec[4]) const { this->mapScalars(vec, vec); }
friend Vector4 operator*(const Matrix44& m, const Vector4& src) {
Vector4 dst;
m.mapScalars(src.fData, dst.fData);
return dst;
}
/**
* map an array of [x, y, 0, 1] through the matrix, returning an array
* of [x', y', z', w'].
*
* @param src2 array of [x, y] pairs, with implied z=0 and w=1
* @param count number of [x, y] pairs in src2
* @param dst4 array of [x', y', z', w'] quads as the output.
*/
void map2(const float src2[], int count, float dst4[]) const;
void map2(const double src2[], int count, double dst4[]) const;
/** Returns true if transformating an axis-aligned square in 2d by this matrix
will produce another 2d axis-aligned square; typically means the matrix
is a scale with perhaps a 90-degree rotation. A 3d rotation through 90
degrees into a perpendicular plane collapses a square to a line, but
is still considered to be axis-aligned.
By default, tolerates very slight error due to float imprecisions;
a 90-degree rotation can still end up with 10^-17 of
"non-axis-aligned" result.
*/
bool preserves2dAxisAlignment(SkScalar epsilon = SK_ScalarNearlyZero) const;
double determinant() const;
void FlattenTo2d();
private:
/* This is indexed by [col][row]. */
SkScalar fMat[4][4];
TypeMask fTypeMask;
static constexpr int kAllPublic_Masks = 0xF;
SkScalar transX() const { return fMat[3][0]; }
SkScalar transY() const { return fMat[3][1]; }
SkScalar transZ() const { return fMat[3][2]; }
SkScalar scaleX() const { return fMat[0][0]; }
SkScalar scaleY() const { return fMat[1][1]; }
SkScalar scaleZ() const { return fMat[2][2]; }
SkScalar perspX() const { return fMat[0][3]; }
SkScalar perspY() const { return fMat[1][3]; }
SkScalar perspZ() const { return fMat[2][3]; }
void recomputeTypeMask();
inline void setTypeMask(TypeMask mask) {
SkASSERT(0 == (~kAllPublic_Masks & mask));
fTypeMask = mask;
}
};
} // namespace gfx
#endif // UI_GFX_GEOMETRY_MATRIX44_H_
|