blob: a720f3e5de3e2477cc8cc5b779da6573847d5aaa [file] [log] [blame]
// 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.
#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 {
// This is the underlying data structure of Transform. Don't use this type
// directly.
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); }
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);
// 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); }
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_