diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index fe0768cb33..87b0d7e54d 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -2,6 +2,8 @@ Skia Graphics Release Notes This file includes a list of high level updates for each milestone release. + * Removed SkMatrix44 + * * * Milestone 93 diff --git a/gn/core.gni b/gn/core.gni index 00c4b16d4c..df6d672f51 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -53,7 +53,6 @@ skia_core_public = [ "$_include/core/SkMaskFilter.h", "$_include/core/SkMath.h", "$_include/core/SkMatrix.h", - "$_include/core/SkMatrix44.h", "$_include/core/SkMilestone.h", "$_include/core/SkOverdrawCanvas.h", "$_include/core/SkPaint.h", @@ -266,7 +265,6 @@ skia_core_sources = [ "$_src/core/SkMath.cpp", "$_src/core/SkMathPriv.h", "$_src/core/SkMatrix.cpp", - "$_src/core/SkMatrix44.cpp", "$_src/core/SkMatrixImageFilter.cpp", "$_src/core/SkMatrixImageFilter.h", "$_src/core/SkMatrixInvert.cpp", diff --git a/gn/tests.gni b/gn/tests.gni index 0123c287fa..402b60c845 100644 --- a/gn/tests.gni +++ b/gn/tests.gni @@ -151,7 +151,6 @@ tests_sources = [ "$_tests/MallocPixelRefTest.cpp", "$_tests/MaskCacheTest.cpp", "$_tests/MathTest.cpp", - "$_tests/Matrix44Test.cpp", "$_tests/MatrixClipCollapseTest.cpp", "$_tests/MatrixColorFilterTest.cpp", "$_tests/MatrixTest.cpp", diff --git a/include/core/SkMatrix44.h b/include/core/SkMatrix44.h deleted file mode 100644 index 622673e53b..0000000000 --- a/include/core/SkMatrix44.h +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkMatrix44_DEFINED -#define SkMatrix44_DEFINED - -#include "include/core/SkMatrix.h" -#include "include/core/SkScalar.h" - -#include -#include - -// This entire file is DEPRECATED, and will be removed at some point. -// SkCanvas has full support for 4x4 matrices using SkM44 - -// DEPRECATED -struct SkVector4 { - SkScalar fData[4]; - - SkVector4() { - this->set(0, 0, 0, 1); - } - SkVector4(const SkVector4& src) { - memcpy(fData, src.fData, sizeof(fData)); - } - SkVector4(SkScalar x, SkScalar y, SkScalar z, SkScalar w = SK_Scalar1) { - fData[0] = x; - fData[1] = y; - fData[2] = z; - fData[3] = w; - } - - SkVector4& operator=(const SkVector4& src) { - memcpy(fData, src.fData, sizeof(fData)); - return *this; - } - - bool operator==(const SkVector4& 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 SkVector4& 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; - } -}; - -// DEPRECATED -class SK_API SkMatrix44 { -public: - - enum Uninitialized_Constructor { - kUninitialized_Constructor - }; - enum Identity_Constructor { - kIdentity_Constructor - }; - enum NaN_Constructor { - kNaN_Constructor - }; - - SkMatrix44(Uninitialized_Constructor) {} // ironically, cannot be constexpr - - constexpr SkMatrix44(Identity_Constructor) - : fMat{{ 1, 0, 0, 0, }, - { 0, 1, 0, 0, }, - { 0, 0, 1, 0, }, - { 0, 0, 0, 1, }} - , fTypeMask(kIdentity_Mask) {} - - SkMatrix44(NaN_Constructor) - : fMat{{ SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN }, - { SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN }, - { SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN }, - { SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN }} - , fTypeMask(kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask) {} - - constexpr SkMatrix44() : SkMatrix44{kIdentity_Constructor} {} - - SkMatrix44(const SkMatrix44& src) = default; - - SkMatrix44& operator=(const SkMatrix44& src) = default; - - SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) { - this->setConcat(a, b); - } - - bool operator==(const SkMatrix44& other) const; - bool operator!=(const SkMatrix44& other) const { - return !(other == *this); - } - - /* When converting from SkMatrix44 to SkMatrix, the third row and - * column is dropped. When converting from SkMatrix to SkMatrix44 - * 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 ] - */ - SkMatrix44(const SkMatrix&); - SkMatrix44& operator=(const SkMatrix& src); - - // TODO: make this explicit (will need to guard that change to update chrome, etc. -#ifndef SK_SUPPORT_LEGACY_IMPLICIT_CONVERSION_MATRIX44 - explicit -#endif - operator SkMatrix() const; - - /** - * Return a reference to a const identity matrix - */ - static const SkMatrix44& I(); - - 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(); - inline void reset() { this->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 get(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 set(int row, int col, SkScalar value) { - SkASSERT((unsigned)row <= 3); - SkASSERT((unsigned)col <= 3); - fMat[col][row] = value; - this->recomputeTypeMask(); - } - - inline double getDouble(int row, int col) const { - return double(this->get(row, col)); - } - inline void setDouble(int row, int col, double value) { - this->set(row, col, SkScalar(value)); - } - inline float getFloat(int row, int col) const { - return float(this->get(row, col)); - } - inline void setFloat(int row, int col, float value) { - this->set(row, col, value); - } - - /** 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 asColMajorf(float[]) const; - void asColMajord(double[]) const; - void asRowMajorf(float[]) const; - void asRowMajord(double[]) 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 setColMajorf(const float[]); - void setColMajord(const double[]); - void setRowMajorf(const float[]); - void setRowMajord(const double[]); - - void setColMajor(const SkScalar data[]) { this->setColMajorf(data); } - void setRowMajor(const SkScalar data[]) { this->setRowMajorf(data); } - - /* This sets the top-left of the matrix and clears the translation and - * perspective components (with [3][3] set to 1). m_ij is interpreted - * as the matrix entry at row = i, col = j. */ - void set3x3(SkScalar m_00, SkScalar m_10, SkScalar m_20, - SkScalar m_01, SkScalar m_11, SkScalar m_21, - SkScalar m_02, SkScalar m_12, SkScalar m_22); - void set3x3RowMajorf(const float[]); - - void set4x4(SkScalar m_00, SkScalar m_10, SkScalar m_20, SkScalar m_30, - SkScalar m_01, SkScalar m_11, SkScalar m_21, SkScalar m_31, - SkScalar m_02, SkScalar m_12, SkScalar m_22, SkScalar m_32, - SkScalar m_03, SkScalar m_13, SkScalar m_23, SkScalar m_33); - - SkMatrix44& setTranslate(SkScalar dx, SkScalar dy, SkScalar dz); - SkMatrix44& preTranslate(SkScalar dx, SkScalar dy, SkScalar dz); - SkMatrix44& postTranslate(SkScalar dx, SkScalar dy, SkScalar dz); - - SkMatrix44& setScale(SkScalar sx, SkScalar sy, SkScalar sz); - SkMatrix44& preScale(SkScalar sx, SkScalar sy, SkScalar sz); - SkMatrix44& postScale(SkScalar sx, SkScalar sy, SkScalar sz); - - inline SkMatrix44& setScale(SkScalar scale) { - return this->setScale(scale, scale, scale); - } - inline SkMatrix44& preScale(SkScalar scale) { - return this->preScale(scale, scale, scale); - } - inline SkMatrix44& postScale(SkScalar scale) { - return this->postScale(scale, scale, scale); - } - - void setRotateDegreesAbout(SkScalar x, SkScalar y, SkScalar z, SkScalar degrees) { - this->setRotateAbout(x, y, z, degrees * SK_ScalarPI / 180); - } - - /** Rotate about the vector [x,y,z]. If that vector is not unit-length, - it will be automatically resized. - */ - void setRotateAbout(SkScalar x, SkScalar y, SkScalar z, SkScalar radians); - /** Rotate about the vector [x,y,z]. Does not check the length of the - vector, assuming it is unit-length. - */ - void setRotateAboutUnit(SkScalar x, SkScalar y, SkScalar z, SkScalar radians); - - void setConcat(const SkMatrix44& a, const SkMatrix44& b); - inline void preConcat(const SkMatrix44& m) { - this->setConcat(*this, m); - } - inline void postConcat(const SkMatrix44& m) { - this->setConcat(m, *this); - } - - friend SkMatrix44 operator*(const SkMatrix44& a, const SkMatrix44& b) { - return SkMatrix44(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(SkMatrix44* 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 SkVector4 operator*(const SkMatrix44& m, const SkVector4& src) { - SkVector4 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; - - void dump() const; - - double determinant() const; - -private: - /* This is indexed by [col][row]. */ - SkScalar fMat[4][4]; - TypeMask fTypeMask; - - static constexpr int kAllPublic_Masks = 0xF; - - void as3x4RowMajorf(float[]) const; - void set3x4RowMajorf(const float[]); - - 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; - } - - inline const SkScalar* values() const { return &fMat[0][0]; } - - friend class SkColorSpace; - friend class SkCanvas; - friend class SkM44; -}; - -#endif diff --git a/src/core/SkMatrix44.cpp b/src/core/SkMatrix44.cpp deleted file mode 100644 index 9942607ede..0000000000 --- a/src/core/SkMatrix44.cpp +++ /dev/null @@ -1,999 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/core/SkMatrix44.h" -#include -#include - -// Copying SkMatrix44 byte-wise is performance-critical to Blink. This class is -// contained in several Transform classes, which are copied multiple times -// during the rendering life cycle. See crbug.com/938563 for reference. -#if defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) -// std::is_trivially_copyable is not supported for some older clang versions, -// which (at least as of this patch) are in use for Chromecast. -static_assert(std::is_trivially_copyable::value, - "SkMatrix44 must be trivially copyable"); -#endif - -static inline bool eq4(const SkScalar* SK_RESTRICT a, - const SkScalar* SK_RESTRICT b) { - return (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]) & (a[3] == b[3]); -} - -bool SkMatrix44::operator==(const SkMatrix44& other) const { - if (this == &other) { - return true; - } - - if (this->isIdentity() && other.isIdentity()) { - return true; - } - - const SkScalar* SK_RESTRICT a = &fMat[0][0]; - const SkScalar* SK_RESTRICT b = &other.fMat[0][0]; - -#if 0 - for (int i = 0; i < 16; ++i) { - if (a[i] != b[i]) { - return false; - } - } - return true; -#else - // to reduce branch instructions, we compare 4 at a time. - // see bench/Matrix44Bench.cpp for test. - if (!eq4(&a[0], &b[0])) { - return false; - } - if (!eq4(&a[4], &b[4])) { - return false; - } - if (!eq4(&a[8], &b[8])) { - return false; - } - return eq4(&a[12], &b[12]); -#endif -} - -/////////////////////////////////////////////////////////////////////////////// -void SkMatrix44::recomputeTypeMask() { - if (0 != perspX() || 0 != perspY() || 0 != perspZ() || 1 != fMat[3][3]) { - fTypeMask = kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask; - return; - } - - TypeMask mask = kIdentity_Mask; - if (0 != transX() || 0 != transY() || 0 != transZ()) { - mask |= kTranslate_Mask; - } - - if (1 != scaleX() || 1 != scaleY() || 1 != scaleZ()) { - mask |= kScale_Mask; - } - - if (0 != fMat[1][0] || 0 != fMat[0][1] || 0 != fMat[0][2] || - 0 != fMat[2][0] || 0 != fMat[1][2] || 0 != fMat[2][1]) { - mask |= kAffine_Mask; - } - fTypeMask = mask; -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkMatrix44::asColMajorf(float dst[]) const { - const SkScalar* src = &fMat[0][0]; - for (int i = 0; i < 16; ++i) { - dst[i] = src[i]; - } -} - -void SkMatrix44::as3x4RowMajorf(float dst[]) const { - dst[0] = fMat[0][0]; dst[1] = fMat[1][0]; dst[2] = fMat[2][0]; dst[3] = fMat[3][0]; - dst[4] = fMat[0][1]; dst[5] = fMat[1][1]; dst[6] = fMat[2][1]; dst[7] = fMat[3][1]; - dst[8] = fMat[0][2]; dst[9] = fMat[1][2]; dst[10] = fMat[2][2]; dst[11] = fMat[3][2]; -} - -void SkMatrix44::asColMajord(double dst[]) const { - const SkScalar* src = &fMat[0][0]; - for (int i = 0; i < 16; ++i) { - dst[i] = src[i]; - } -} - -void SkMatrix44::asRowMajorf(float dst[]) const { - const SkScalar* src = &fMat[0][0]; - for (int i = 0; i < 4; ++i) { - dst[0] = float(src[0]); - dst[4] = float(src[1]); - dst[8] = float(src[2]); - dst[12] = float(src[3]); - src += 4; - dst += 1; - } -} - -void SkMatrix44::asRowMajord(double dst[]) const { - const SkScalar* src = &fMat[0][0]; - for (int i = 0; i < 4; ++i) { - dst[0] = src[0]; - dst[4] = src[1]; - dst[8] = src[2]; - dst[12] = src[3]; - src += 4; - dst += 1; - } -} - -void SkMatrix44::setColMajorf(const float src[]) { - SkScalar* dst = &fMat[0][0]; - for (int i = 0; i < 16; ++i) { - dst[i] = src[i]; - } - - this->recomputeTypeMask(); -} - -void SkMatrix44::setColMajord(const double src[]) { - SkScalar* dst = &fMat[0][0]; - for (int i = 0; i < 16; ++i) { - dst[i] = SkScalar(src[i]); - } - - this->recomputeTypeMask(); -} - -void SkMatrix44::setRowMajorf(const float src[]) { - SkScalar* dst = &fMat[0][0]; - for (int i = 0; i < 4; ++i) { - dst[0] = src[0]; - dst[4] = src[1]; - dst[8] = src[2]; - dst[12] = src[3]; - src += 4; - dst += 1; - } - this->recomputeTypeMask(); -} - -void SkMatrix44::setRowMajord(const double src[]) { - SkScalar* dst = &fMat[0][0]; - for (int i = 0; i < 4; ++i) { - dst[0] = SkScalar(src[0]); - dst[4] = SkScalar(src[1]); - dst[8] = SkScalar(src[2]); - dst[12] = SkScalar(src[3]); - src += 4; - dst += 1; - } - this->recomputeTypeMask(); -} - -/////////////////////////////////////////////////////////////////////////////// - -const SkMatrix44& SkMatrix44::I() { - static constexpr SkMatrix44 gIdentity44(kIdentity_Constructor); - return gIdentity44; -} - -void SkMatrix44::setIdentity() { - fMat[0][0] = 1; - fMat[0][1] = 0; - fMat[0][2] = 0; - fMat[0][3] = 0; - fMat[1][0] = 0; - fMat[1][1] = 1; - fMat[1][2] = 0; - fMat[1][3] = 0; - fMat[2][0] = 0; - fMat[2][1] = 0; - fMat[2][2] = 1; - fMat[2][3] = 0; - fMat[3][0] = 0; - fMat[3][1] = 0; - fMat[3][2] = 0; - fMat[3][3] = 1; - this->setTypeMask(kIdentity_Mask); -} - -void SkMatrix44::set3x3(SkScalar m_00, SkScalar m_10, SkScalar m_20, - SkScalar m_01, SkScalar m_11, SkScalar m_21, - SkScalar m_02, SkScalar m_12, SkScalar m_22) { - fMat[0][0] = m_00; fMat[0][1] = m_10; fMat[0][2] = m_20; fMat[0][3] = 0; - fMat[1][0] = m_01; fMat[1][1] = m_11; fMat[1][2] = m_21; fMat[1][3] = 0; - fMat[2][0] = m_02; fMat[2][1] = m_12; fMat[2][2] = m_22; fMat[2][3] = 0; - fMat[3][0] = 0; fMat[3][1] = 0; fMat[3][2] = 0; fMat[3][3] = 1; - this->recomputeTypeMask(); -} - -void SkMatrix44::set3x3RowMajorf(const float src[]) { - fMat[0][0] = src[0]; fMat[0][1] = src[3]; fMat[0][2] = src[6]; fMat[0][3] = 0; - fMat[1][0] = src[1]; fMat[1][1] = src[4]; fMat[1][2] = src[7]; fMat[1][3] = 0; - fMat[2][0] = src[2]; fMat[2][1] = src[5]; fMat[2][2] = src[8]; fMat[2][3] = 0; - fMat[3][0] = 0; fMat[3][1] = 0; fMat[3][2] = 0; fMat[3][3] = 1; - this->recomputeTypeMask(); -} - -void SkMatrix44::set3x4RowMajorf(const float src[]) { - fMat[0][0] = src[0]; fMat[1][0] = src[1]; fMat[2][0] = src[2]; fMat[3][0] = src[3]; - fMat[0][1] = src[4]; fMat[1][1] = src[5]; fMat[2][1] = src[6]; fMat[3][1] = src[7]; - fMat[0][2] = src[8]; fMat[1][2] = src[9]; fMat[2][2] = src[10]; fMat[3][2] = src[11]; - fMat[0][3] = 0; fMat[1][3] = 0; fMat[2][3] = 0; fMat[3][3] = 1; - this->recomputeTypeMask(); -} - -void SkMatrix44::set4x4(SkScalar m_00, SkScalar m_10, SkScalar m_20, SkScalar m_30, - SkScalar m_01, SkScalar m_11, SkScalar m_21, SkScalar m_31, - SkScalar m_02, SkScalar m_12, SkScalar m_22, SkScalar m_32, - SkScalar m_03, SkScalar m_13, SkScalar m_23, SkScalar m_33) { - fMat[0][0] = m_00; fMat[0][1] = m_10; fMat[0][2] = m_20; fMat[0][3] = m_30; - fMat[1][0] = m_01; fMat[1][1] = m_11; fMat[1][2] = m_21; fMat[1][3] = m_31; - fMat[2][0] = m_02; fMat[2][1] = m_12; fMat[2][2] = m_22; fMat[2][3] = m_32; - fMat[3][0] = m_03; fMat[3][1] = m_13; fMat[3][2] = m_23; fMat[3][3] = m_33; - this->recomputeTypeMask(); -} - - -/////////////////////////////////////////////////////////////////////////////// - -SkMatrix44& SkMatrix44::setTranslate(SkScalar dx, SkScalar dy, SkScalar dz) { - this->setIdentity(); - - if (!dx && !dy && !dz) { - return *this; - } - - fMat[3][0] = dx; - fMat[3][1] = dy; - fMat[3][2] = dz; - this->setTypeMask(kTranslate_Mask); - return *this; -} - -SkMatrix44& SkMatrix44::preTranslate(SkScalar dx, SkScalar dy, SkScalar dz) { - if (!dx && !dy && !dz) { - return *this; - } - - for (int i = 0; i < 4; ++i) { - fMat[3][i] = fMat[0][i] * dx + fMat[1][i] * dy + fMat[2][i] * dz + fMat[3][i]; - } - this->recomputeTypeMask(); - return *this; -} - -SkMatrix44& SkMatrix44::postTranslate(SkScalar dx, SkScalar dy, SkScalar dz) { - if (!dx && !dy && !dz) { - return *this; - } - - if (this->getType() & kPerspective_Mask) { - for (int i = 0; i < 4; ++i) { - fMat[i][0] += fMat[i][3] * dx; - fMat[i][1] += fMat[i][3] * dy; - fMat[i][2] += fMat[i][3] * dz; - } - } else { - fMat[3][0] += dx; - fMat[3][1] += dy; - fMat[3][2] += dz; - this->recomputeTypeMask(); - } - return *this; -} - -/////////////////////////////////////////////////////////////////////////////// - -SkMatrix44& SkMatrix44::setScale(SkScalar sx, SkScalar sy, SkScalar sz) { - this->setIdentity(); - - if (1 == sx && 1 == sy && 1 == sz) { - return *this; - } - - fMat[0][0] = sx; - fMat[1][1] = sy; - fMat[2][2] = sz; - this->setTypeMask(kScale_Mask); - return *this; -} - -SkMatrix44& SkMatrix44::preScale(SkScalar sx, SkScalar sy, SkScalar sz) { - if (1 == sx && 1 == sy && 1 == sz) { - return *this; - } - - // The implementation matrix * pureScale can be shortcut - // by knowing that pureScale components effectively scale - // the columns of the original matrix. - for (int i = 0; i < 4; i++) { - fMat[0][i] *= sx; - fMat[1][i] *= sy; - fMat[2][i] *= sz; - } - this->recomputeTypeMask(); - return *this; -} - -SkMatrix44& SkMatrix44::postScale(SkScalar sx, SkScalar sy, SkScalar sz) { - if (1 == sx && 1 == sy && 1 == sz) { - return *this; - } - - for (int i = 0; i < 4; i++) { - fMat[i][0] *= sx; - fMat[i][1] *= sy; - fMat[i][2] *= sz; - } - this->recomputeTypeMask(); - return *this; -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkMatrix44::setRotateAbout(SkScalar x, SkScalar y, SkScalar z, SkScalar radians) { - double len2 = (double)x * x + (double)y * y + (double)z * z; - if (1 != len2) { - if (0 == len2) { - this->setIdentity(); - return; - } - double scale = 1 / sqrt(len2); - x = SkScalar(x * scale); - y = SkScalar(y * scale); - z = SkScalar(z * scale); - } - this->setRotateAboutUnit(x, y, z, radians); -} - -void SkMatrix44::setRotateAboutUnit(SkScalar x, SkScalar y, SkScalar z, SkScalar radians) { - double c = cos(radians); - double s = sin(radians); - double C = 1 - c; - double xs = x * s; - double ys = y * s; - double zs = z * s; - double xC = x * C; - double yC = y * C; - double zC = z * C; - double xyC = x * yC; - double yzC = y * zC; - double zxC = z * xC; - - // if you're looking at wikipedia, remember that we're column major. - this->set3x3(SkScalar(x * xC + c), // scale x - SkScalar(xyC + zs), // skew x - SkScalar(zxC - ys), // trans x - - SkScalar(xyC - zs), // skew y - SkScalar(y * yC + c), // scale y - SkScalar(yzC + xs), // trans y - - SkScalar(zxC + ys), // persp x - SkScalar(yzC - xs), // persp y - SkScalar(z * zC + c)); // persp 2 -} - -/////////////////////////////////////////////////////////////////////////////// - -static bool bits_isonly(int value, int mask) { - return 0 == (value & ~mask); -} - -void SkMatrix44::setConcat(const SkMatrix44& a, const SkMatrix44& b) { - const SkMatrix44::TypeMask a_mask = a.getType(); - const SkMatrix44::TypeMask b_mask = b.getType(); - - if (kIdentity_Mask == a_mask) { - *this = b; - return; - } - if (kIdentity_Mask == b_mask) { - *this = a; - return; - } - - bool useStorage = (this == &a || this == &b); - SkScalar storage[16]; - SkScalar* result = useStorage ? storage : &fMat[0][0]; - - // Both matrices are at most scale+translate - if (bits_isonly(a_mask | b_mask, kScale_Mask | kTranslate_Mask)) { - result[0] = a.fMat[0][0] * b.fMat[0][0]; - result[1] = result[2] = result[3] = result[4] = 0; - result[5] = a.fMat[1][1] * b.fMat[1][1]; - result[6] = result[7] = result[8] = result[9] = 0; - result[10] = a.fMat[2][2] * b.fMat[2][2]; - result[11] = 0; - result[12] = a.fMat[0][0] * b.fMat[3][0] + a.fMat[3][0]; - result[13] = a.fMat[1][1] * b.fMat[3][1] + a.fMat[3][1]; - result[14] = a.fMat[2][2] * b.fMat[3][2] + a.fMat[3][2]; - result[15] = 1; - } else { - for (int j = 0; j < 4; j++) { - for (int i = 0; i < 4; i++) { - double value = 0; - for (int k = 0; k < 4; k++) { - value += double(a.fMat[k][i]) * b.fMat[j][k]; - } - *result++ = SkScalar(value); - } - } - } - - if (useStorage) { - memcpy(fMat, storage, sizeof(storage)); - } - this->recomputeTypeMask(); -} - -/////////////////////////////////////////////////////////////////////////////// - -/** We always perform the calculation in doubles, to avoid prematurely losing - precision along the way. This relies on the compiler automatically - promoting our SkScalar values to double (if needed). - */ -double SkMatrix44::determinant() const { - if (this->isIdentity()) { - return 1; - } - if (this->isScaleTranslate()) { - return fMat[0][0] * fMat[1][1] * fMat[2][2] * fMat[3][3]; - } - - double a00 = fMat[0][0]; - double a01 = fMat[0][1]; - double a02 = fMat[0][2]; - double a03 = fMat[0][3]; - double a10 = fMat[1][0]; - double a11 = fMat[1][1]; - double a12 = fMat[1][2]; - double a13 = fMat[1][3]; - double a20 = fMat[2][0]; - double a21 = fMat[2][1]; - double a22 = fMat[2][2]; - double a23 = fMat[2][3]; - double a30 = fMat[3][0]; - double a31 = fMat[3][1]; - double a32 = fMat[3][2]; - double a33 = fMat[3][3]; - - double b00 = a00 * a11 - a01 * a10; - double b01 = a00 * a12 - a02 * a10; - double b02 = a00 * a13 - a03 * a10; - double b03 = a01 * a12 - a02 * a11; - double b04 = a01 * a13 - a03 * a11; - double b05 = a02 * a13 - a03 * a12; - double b06 = a20 * a31 - a21 * a30; - double b07 = a20 * a32 - a22 * a30; - double b08 = a20 * a33 - a23 * a30; - double b09 = a21 * a32 - a22 * a31; - double b10 = a21 * a33 - a23 * a31; - double b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; -} - -/////////////////////////////////////////////////////////////////////////////// - -static bool is_matrix_finite(const SkMatrix44& matrix) { - SkScalar accumulator = 0; - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - accumulator *= matrix.get(row, col); - } - } - return accumulator == 0; -} - -bool SkMatrix44::invert(SkMatrix44* storage) const { - if (this->isIdentity()) { - if (storage) { - storage->setIdentity(); - } - return true; - } - - if (this->isTranslate()) { - if (storage) { - storage->setTranslate(-fMat[3][0], -fMat[3][1], -fMat[3][2]); - } - return true; - } - - SkMatrix44 tmp; - // Use storage if it's available and distinct from this matrix. - SkMatrix44* inverse = (storage && storage != this) ? storage : &tmp; - if (this->isScaleTranslate()) { - if (0 == fMat[0][0] * fMat[1][1] * fMat[2][2]) { - return false; - } - - double invXScale = 1 / fMat[0][0]; - double invYScale = 1 / fMat[1][1]; - double invZScale = 1 / fMat[2][2]; - - inverse->fMat[0][0] = SkDoubleToScalar(invXScale); - inverse->fMat[0][1] = 0; - inverse->fMat[0][2] = 0; - inverse->fMat[0][3] = 0; - - inverse->fMat[1][0] = 0; - inverse->fMat[1][1] = SkDoubleToScalar(invYScale); - inverse->fMat[1][2] = 0; - inverse->fMat[1][3] = 0; - - inverse->fMat[2][0] = 0; - inverse->fMat[2][1] = 0; - inverse->fMat[2][2] = SkDoubleToScalar(invZScale); - inverse->fMat[2][3] = 0; - - inverse->fMat[3][0] = SkDoubleToScalar(-fMat[3][0] * invXScale); - inverse->fMat[3][1] = SkDoubleToScalar(-fMat[3][1] * invYScale); - inverse->fMat[3][2] = SkDoubleToScalar(-fMat[3][2] * invZScale); - inverse->fMat[3][3] = 1; - - inverse->setTypeMask(this->getType()); - - if (!is_matrix_finite(*inverse)) { - return false; - } - if (storage && inverse != storage) { - *storage = *inverse; - } - return true; - } - - double a00 = fMat[0][0]; - double a01 = fMat[0][1]; - double a02 = fMat[0][2]; - double a03 = fMat[0][3]; - double a10 = fMat[1][0]; - double a11 = fMat[1][1]; - double a12 = fMat[1][2]; - double a13 = fMat[1][3]; - double a20 = fMat[2][0]; - double a21 = fMat[2][1]; - double a22 = fMat[2][2]; - double a23 = fMat[2][3]; - double a30 = fMat[3][0]; - double a31 = fMat[3][1]; - double a32 = fMat[3][2]; - double a33 = fMat[3][3]; - - if (!(this->getType() & kPerspective_Mask)) { - // If we know the matrix has no perspective, then the perspective - // component is (0, 0, 0, 1). We can use this information to save a lot - // of arithmetic that would otherwise be spent to compute the inverse - // of a general matrix. - - SkASSERT(a03 == 0); - SkASSERT(a13 == 0); - SkASSERT(a23 == 0); - SkASSERT(a33 == 1); - - double b00 = a00 * a11 - a01 * a10; - double b01 = a00 * a12 - a02 * a10; - double b03 = a01 * a12 - a02 * a11; - double b06 = a20 * a31 - a21 * a30; - double b07 = a20 * a32 - a22 * a30; - double b08 = a20; - double b09 = a21 * a32 - a22 * a31; - double b10 = a21; - double b11 = a22; - - // Calculate the determinant - double det = b00 * b11 - b01 * b10 + b03 * b08; - - double invdet = sk_ieee_double_divide(1.0, det); - // If det is zero, we want to return false. However, we also want to return false - // if 1/det overflows to infinity (i.e. det is denormalized). Both of these are - // handled by checking that 1/det is finite. - if (!sk_float_isfinite(sk_double_to_float(invdet))) { - return false; - } - - b00 *= invdet; - b01 *= invdet; - b03 *= invdet; - b06 *= invdet; - b07 *= invdet; - b08 *= invdet; - b09 *= invdet; - b10 *= invdet; - b11 *= invdet; - - inverse->fMat[0][0] = SkDoubleToScalar(a11 * b11 - a12 * b10); - inverse->fMat[0][1] = SkDoubleToScalar(a02 * b10 - a01 * b11); - inverse->fMat[0][2] = SkDoubleToScalar(b03); - inverse->fMat[0][3] = 0; - inverse->fMat[1][0] = SkDoubleToScalar(a12 * b08 - a10 * b11); - inverse->fMat[1][1] = SkDoubleToScalar(a00 * b11 - a02 * b08); - inverse->fMat[1][2] = SkDoubleToScalar(-b01); - inverse->fMat[1][3] = 0; - inverse->fMat[2][0] = SkDoubleToScalar(a10 * b10 - a11 * b08); - inverse->fMat[2][1] = SkDoubleToScalar(a01 * b08 - a00 * b10); - inverse->fMat[2][2] = SkDoubleToScalar(b00); - inverse->fMat[2][3] = 0; - inverse->fMat[3][0] = SkDoubleToScalar(a11 * b07 - a10 * b09 - a12 * b06); - inverse->fMat[3][1] = SkDoubleToScalar(a00 * b09 - a01 * b07 + a02 * b06); - inverse->fMat[3][2] = SkDoubleToScalar(a31 * b01 - a30 * b03 - a32 * b00); - inverse->fMat[3][3] = 1; - - inverse->setTypeMask(this->getType()); - if (!is_matrix_finite(*inverse)) { - return false; - } - if (storage && inverse != storage) { - *storage = *inverse; - } - return true; - } - - double b00 = a00 * a11 - a01 * a10; - double b01 = a00 * a12 - a02 * a10; - double b02 = a00 * a13 - a03 * a10; - double b03 = a01 * a12 - a02 * a11; - double b04 = a01 * a13 - a03 * a11; - double b05 = a02 * a13 - a03 * a12; - double b06 = a20 * a31 - a21 * a30; - double b07 = a20 * a32 - a22 * a30; - double b08 = a20 * a33 - a23 * a30; - double b09 = a21 * a32 - a22 * a31; - double b10 = a21 * a33 - a23 * a31; - double b11 = a22 * a33 - a23 * a32; - - // Calculate the determinant - double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - double invdet = sk_ieee_double_divide(1.0, det); - // If det is zero, we want to return false. However, we also want to return false - // if 1/det overflows to infinity (i.e. det is denormalized). Both of these are - // handled by checking that 1/det is finite. - if (!sk_float_isfinite(sk_double_to_float(invdet))) { - return false; - } - - b00 *= invdet; - b01 *= invdet; - b02 *= invdet; - b03 *= invdet; - b04 *= invdet; - b05 *= invdet; - b06 *= invdet; - b07 *= invdet; - b08 *= invdet; - b09 *= invdet; - b10 *= invdet; - b11 *= invdet; - - inverse->fMat[0][0] = SkDoubleToScalar(a11 * b11 - a12 * b10 + a13 * b09); - inverse->fMat[0][1] = SkDoubleToScalar(a02 * b10 - a01 * b11 - a03 * b09); - inverse->fMat[0][2] = SkDoubleToScalar(a31 * b05 - a32 * b04 + a33 * b03); - inverse->fMat[0][3] = SkDoubleToScalar(a22 * b04 - a21 * b05 - a23 * b03); - inverse->fMat[1][0] = SkDoubleToScalar(a12 * b08 - a10 * b11 - a13 * b07); - inverse->fMat[1][1] = SkDoubleToScalar(a00 * b11 - a02 * b08 + a03 * b07); - inverse->fMat[1][2] = SkDoubleToScalar(a32 * b02 - a30 * b05 - a33 * b01); - inverse->fMat[1][3] = SkDoubleToScalar(a20 * b05 - a22 * b02 + a23 * b01); - inverse->fMat[2][0] = SkDoubleToScalar(a10 * b10 - a11 * b08 + a13 * b06); - inverse->fMat[2][1] = SkDoubleToScalar(a01 * b08 - a00 * b10 - a03 * b06); - inverse->fMat[2][2] = SkDoubleToScalar(a30 * b04 - a31 * b02 + a33 * b00); - inverse->fMat[2][3] = SkDoubleToScalar(a21 * b02 - a20 * b04 - a23 * b00); - inverse->fMat[3][0] = SkDoubleToScalar(a11 * b07 - a10 * b09 - a12 * b06); - inverse->fMat[3][1] = SkDoubleToScalar(a00 * b09 - a01 * b07 + a02 * b06); - inverse->fMat[3][2] = SkDoubleToScalar(a31 * b01 - a30 * b03 - a32 * b00); - inverse->fMat[3][3] = SkDoubleToScalar(a20 * b03 - a21 * b01 + a22 * b00); - inverse->setTypeMask(this->getType()); - if (!is_matrix_finite(*inverse)) { - return false; - } - if (storage && inverse != storage) { - *storage = *inverse; - } - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkMatrix44::transpose() { - if (!this->isIdentity()) { - using std::swap; - swap(fMat[0][1], fMat[1][0]); - swap(fMat[0][2], fMat[2][0]); - swap(fMat[0][3], fMat[3][0]); - swap(fMat[1][2], fMat[2][1]); - swap(fMat[1][3], fMat[3][1]); - swap(fMat[2][3], fMat[3][2]); - this->recomputeTypeMask(); - } -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkMatrix44::mapScalars(const SkScalar src[4], SkScalar dst[4]) const { - SkScalar storage[4]; - SkScalar* result = (src == dst) ? storage : dst; - - for (int i = 0; i < 4; i++) { - SkScalar value = 0; - for (int j = 0; j < 4; j++) { - value += fMat[j][i] * src[j]; - } - result[i] = value; - } - - if (storage == result) { - memcpy(dst, storage, sizeof(storage)); - } -} - -typedef void (*Map2Procf)(const SkScalar mat[][4], const float src2[], int count, float dst4[]); -typedef void (*Map2Procd)(const SkScalar mat[][4], const double src2[], int count, double dst4[]); - -static void map2_if(const SkScalar mat[][4], const float* SK_RESTRICT src2, - int count, float* SK_RESTRICT dst4) { - for (int i = 0; i < count; ++i) { - dst4[0] = src2[0]; - dst4[1] = src2[1]; - dst4[2] = 0; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_id(const SkScalar mat[][4], const double* SK_RESTRICT src2, - int count, double* SK_RESTRICT dst4) { - for (int i = 0; i < count; ++i) { - dst4[0] = src2[0]; - dst4[1] = src2[1]; - dst4[2] = 0; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_tf(const SkScalar mat[][4], const float* SK_RESTRICT src2, - int count, float* SK_RESTRICT dst4) { - const float mat30 = float(mat[3][0]); - const float mat31 = float(mat[3][1]); - const float mat32 = float(mat[3][2]); - for (int n = 0; n < count; ++n) { - dst4[0] = src2[0] + mat30; - dst4[1] = src2[1] + mat31; - dst4[2] = mat32; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_td(const SkScalar mat[][4], const double* SK_RESTRICT src2, - int count, double* SK_RESTRICT dst4) { - for (int n = 0; n < count; ++n) { - dst4[0] = src2[0] + mat[3][0]; - dst4[1] = src2[1] + mat[3][1]; - dst4[2] = mat[3][2]; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_sf(const SkScalar mat[][4], const float* SK_RESTRICT src2, - int count, float* SK_RESTRICT dst4) { - const float mat32 = float(mat[3][2]); - for (int n = 0; n < count; ++n) { - dst4[0] = float(mat[0][0] * src2[0] + mat[3][0]); - dst4[1] = float(mat[1][1] * src2[1] + mat[3][1]); - dst4[2] = mat32; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_sd(const SkScalar mat[][4], const double* SK_RESTRICT src2, - int count, double* SK_RESTRICT dst4) { - for (int n = 0; n < count; ++n) { - dst4[0] = mat[0][0] * src2[0] + mat[3][0]; - dst4[1] = mat[1][1] * src2[1] + mat[3][1]; - dst4[2] = mat[3][2]; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_af(const SkScalar mat[][4], const float* SK_RESTRICT src2, - int count, float* SK_RESTRICT dst4) { - SkScalar r; - for (int n = 0; n < count; ++n) { - SkScalar sx = src2[0]; - SkScalar sy = src2[1]; - r = mat[0][0] * sx + mat[1][0] * sy + mat[3][0]; - dst4[0] = float(r); - r = mat[0][1] * sx + mat[1][1] * sy + mat[3][1]; - dst4[1] = float(r); - r = mat[0][2] * sx + mat[1][2] * sy + mat[3][2]; - dst4[2] = float(r); - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_ad(const SkScalar mat[][4], const double* SK_RESTRICT src2, - int count, double* SK_RESTRICT dst4) { - for (int n = 0; n < count; ++n) { - double sx = src2[0]; - double sy = src2[1]; - dst4[0] = mat[0][0] * sx + mat[1][0] * sy + mat[3][0]; - dst4[1] = mat[0][1] * sx + mat[1][1] * sy + mat[3][1]; - dst4[2] = mat[0][2] * sx + mat[1][2] * sy + mat[3][2]; - dst4[3] = 1; - src2 += 2; - dst4 += 4; - } -} - -static void map2_pf(const SkScalar mat[][4], const float* SK_RESTRICT src2, - int count, float* SK_RESTRICT dst4) { - SkScalar r; - for (int n = 0; n < count; ++n) { - SkScalar sx = src2[0]; - SkScalar sy = src2[1]; - for (int i = 0; i < 4; i++) { - r = mat[0][i] * sx + mat[1][i] * sy + mat[3][i]; - dst4[i] = float(r); - } - src2 += 2; - dst4 += 4; - } -} - -static void map2_pd(const SkScalar mat[][4], const double* SK_RESTRICT src2, - int count, double* SK_RESTRICT dst4) { - for (int n = 0; n < count; ++n) { - double sx = src2[0]; - double sy = src2[1]; - for (int i = 0; i < 4; i++) { - dst4[i] = mat[0][i] * sx + mat[1][i] * sy + mat[3][i]; - } - src2 += 2; - dst4 += 4; - } -} - -void SkMatrix44::map2(const float src2[], int count, float dst4[]) const { - static const Map2Procf gProc[] = { - map2_if, map2_tf, map2_sf, map2_sf, map2_af, map2_af, map2_af, map2_af - }; - - TypeMask mask = this->getType(); - Map2Procf proc = (mask & kPerspective_Mask) ? map2_pf : gProc[mask]; - proc(fMat, src2, count, dst4); -} - -void SkMatrix44::map2(const double src2[], int count, double dst4[]) const { - static const Map2Procd gProc[] = { - map2_id, map2_td, map2_sd, map2_sd, map2_ad, map2_ad, map2_ad, map2_ad - }; - - TypeMask mask = this->getType(); - Map2Procd proc = (mask & kPerspective_Mask) ? map2_pd : gProc[mask]; - proc(fMat, src2, count, dst4); -} - -bool SkMatrix44::preserves2dAxisAlignment (SkScalar epsilon) const { - - // Can't check (mask & kPerspective_Mask) because Z isn't relevant here. - if (0 != perspX() || 0 != perspY()) return false; - - // A matrix with two non-zeroish values in any of the upper right - // rows or columns will skew. If only one value in each row or - // column is non-zeroish, we get a scale plus perhaps a 90-degree - // rotation. - int col0 = 0; - int col1 = 0; - int row0 = 0; - int row1 = 0; - - // Must test against epsilon, not 0, because we can get values - // around 6e-17 in the matrix that "should" be 0. - - if (SkScalarAbs(fMat[0][0]) > epsilon) { - col0++; - row0++; - } - if (SkScalarAbs(fMat[0][1]) > epsilon) { - col1++; - row0++; - } - if (SkScalarAbs(fMat[1][0]) > epsilon) { - col0++; - row1++; - } - if (SkScalarAbs(fMat[1][1]) > epsilon) { - col1++; - row1++; - } - if (col0 > 1 || col1 > 1 || row0 > 1 || row1 > 1) { - return false; - } - - return true; -} - -/////////////////////////////////////////////////////////////////////////////// - -void SkMatrix44::dump() const { - static const char* format = "|%g %g %g %g|\n" - "|%g %g %g %g|\n" - "|%g %g %g %g|\n" - "|%g %g %g %g|\n"; - SkDebugf(format, - fMat[0][0], fMat[1][0], fMat[2][0], fMat[3][0], - fMat[0][1], fMat[1][1], fMat[2][1], fMat[3][1], - fMat[0][2], fMat[1][2], fMat[2][2], fMat[3][2], - fMat[0][3], fMat[1][3], fMat[2][3], fMat[3][3]); -} - -/////////////////////////////////////////////////////////////////////////////// - -static void initFromMatrix(SkScalar dst[4][4], const SkMatrix& src) { - dst[0][0] = src[SkMatrix::kMScaleX]; - dst[1][0] = src[SkMatrix::kMSkewX]; - dst[2][0] = 0; - dst[3][0] = src[SkMatrix::kMTransX]; - dst[0][1] = src[SkMatrix::kMSkewY]; - dst[1][1] = src[SkMatrix::kMScaleY]; - dst[2][1] = 0; - dst[3][1] = src[SkMatrix::kMTransY]; - dst[0][2] = 0; - dst[1][2] = 0; - dst[2][2] = 1; - dst[3][2] = 0; - dst[0][3] = src[SkMatrix::kMPersp0]; - dst[1][3] = src[SkMatrix::kMPersp1]; - dst[2][3] = 0; - dst[3][3] = src[SkMatrix::kMPersp2]; -} - -SkMatrix44::SkMatrix44(const SkMatrix& src) { - this->operator=(src); -} - -SkMatrix44& SkMatrix44::operator=(const SkMatrix& src) { - initFromMatrix(fMat, src); - - if (src.isIdentity()) { - this->setTypeMask(kIdentity_Mask); - } else { - this->recomputeTypeMask(); - } - return *this; -} - -SkMatrix44::operator SkMatrix() const { - SkMatrix dst; - - dst[SkMatrix::kMScaleX] = fMat[0][0]; - dst[SkMatrix::kMSkewX] = fMat[1][0]; - dst[SkMatrix::kMTransX] = fMat[3][0]; - - dst[SkMatrix::kMSkewY] = fMat[0][1]; - dst[SkMatrix::kMScaleY] = fMat[1][1]; - dst[SkMatrix::kMTransY] = fMat[3][1]; - - dst[SkMatrix::kMPersp0] = fMat[0][3]; - dst[SkMatrix::kMPersp1] = fMat[1][3]; - dst[SkMatrix::kMPersp2] = fMat[3][3]; - - return dst; -} diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp deleted file mode 100644 index 28f7d762a5..0000000000 --- a/tests/Matrix44Test.cpp +++ /dev/null @@ -1,926 +0,0 @@ -/* - * Copyright 2011 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "include/core/SkMatrix44.h" -#include "include/core/SkPoint3.h" -#include "tests/Test.h" - -static bool nearly_equal_double(double a, double b) { - const double tolerance = 1e-7; - double diff = a - b; - if (diff < 0) - diff = -diff; - return diff <= tolerance; -} - -static bool nearly_equal_scalar(SkScalar a, SkScalar b) { - const SkScalar tolerance = SK_Scalar1 / 200000; - return SkScalarAbs(a - b) <= tolerance; -} - -template void assert16(skiatest::Reporter* reporter, const T data[], - T m0, T m1, T m2, T m3, - T m4, T m5, T m6, T m7, - T m8, T m9, T m10, T m11, - T m12, T m13, T m14, T m15) { - REPORTER_ASSERT(reporter, data[0] == m0); - REPORTER_ASSERT(reporter, data[1] == m1); - REPORTER_ASSERT(reporter, data[2] == m2); - REPORTER_ASSERT(reporter, data[3] == m3); - - REPORTER_ASSERT(reporter, data[4] == m4); - REPORTER_ASSERT(reporter, data[5] == m5); - REPORTER_ASSERT(reporter, data[6] == m6); - REPORTER_ASSERT(reporter, data[7] == m7); - - REPORTER_ASSERT(reporter, data[8] == m8); - REPORTER_ASSERT(reporter, data[9] == m9); - REPORTER_ASSERT(reporter, data[10] == m10); - REPORTER_ASSERT(reporter, data[11] == m11); - - REPORTER_ASSERT(reporter, data[12] == m12); - REPORTER_ASSERT(reporter, data[13] == m13); - REPORTER_ASSERT(reporter, data[14] == m14); - REPORTER_ASSERT(reporter, data[15] == m15); -} - -static bool nearly_equal(const SkMatrix44& a, const SkMatrix44& b) { - for (int i = 0; i < 4; ++i) { - for (int j = 0; j < 4; ++j) { - if (!SkScalarNearlyEqual(a.get(i, j), b.get(i, j))) { - SkDebugf("not equal %g %g\n", a.get(i, j), b.get(i, j)); - return false; - } - } - } - return true; -} - -static bool is_identity(const SkMatrix44& m) { - SkMatrix44 identity(SkMatrix44::kIdentity_Constructor); - return nearly_equal(m, identity); -} - -/////////////////////////////////////////////////////////////////////////////// -static bool bits_isonly(int value, int mask) { - return 0 == (value & ~mask); -} - -static void test_constructor(skiatest::Reporter* reporter) { - // Allocate a matrix on the heap - SkMatrix44* placeholderMatrix = new SkMatrix44; - std::unique_ptr deleteMe(placeholderMatrix); - - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - placeholderMatrix->setDouble(row, col, row * col); - } - } - - // Use placement-new syntax to trigger the constructor on top of the heap - // address we already initialized. This allows us to check that the - // constructor did avoid initializing the matrix contents. - SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor); - REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); - REPORTER_ASSERT(reporter, !testMatrix->isIdentity()); - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col))); - } - } - - // Verify that kIdentity_Constructor really does initialize to an identity matrix. - testMatrix = nullptr; - testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor); - REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix); - REPORTER_ASSERT(reporter, testMatrix->isIdentity()); - REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); - - // Verify that that constructing from an SkMatrix initializes everything. - SkMatrix44 scaleMatrix; - scaleMatrix.setScale(3, 4, 5); - REPORTER_ASSERT(reporter, scaleMatrix.isScale()); - testMatrix = new(&scaleMatrix) SkMatrix44(SkMatrix::I()); - REPORTER_ASSERT(reporter, testMatrix->isIdentity()); - REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I()); -} - -static void test_translate(skiatest::Reporter* reporter) { - SkMatrix44 mat; - SkMatrix44 inverse; - - mat.setTranslate(0, 0, 0); - REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); - mat.setTranslate(1, 2, 3); - REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask)); - REPORTER_ASSERT(reporter, mat.invert(&inverse)); - REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask)); - - SkMatrix44 a,b,c; - a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); - b.setTranslate(10, 11, 12); - - c.setConcat(a, b); - mat = a; - mat.preTranslate(10, 11, 12); - REPORTER_ASSERT(reporter, mat == c); - - c.setConcat(b, a); - mat = a; - mat.postTranslate(10, 11, 12); - REPORTER_ASSERT(reporter, mat == c); -} - -static void test_scale(skiatest::Reporter* reporter) { - SkMatrix44 mat; - SkMatrix44 inverse; - - mat.setScale(1, 1, 1); - REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask)); - mat.setScale(1, 2, 3); - REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask)); - REPORTER_ASSERT(reporter, mat.invert(&inverse)); - REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask)); - - SkMatrix44 a,b,c; - a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9); - b.setScale(10, 11, 12); - - c.setConcat(a, b); - mat = a; - mat.preScale(10, 11, 12); - REPORTER_ASSERT(reporter, mat == c); - - c.setConcat(b, a); - mat = a; - mat.postScale(10, 11, 12); - REPORTER_ASSERT(reporter, mat == c); -} - -static void make_i(SkMatrix44* mat) { mat->setIdentity(); } -static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); } -static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); } -static void make_st(SkMatrix44* mat) { - mat->setScale(1, 2, 3); - mat->postTranslate(1, 2, 3); -} -static void make_a(SkMatrix44* mat) { - mat->setRotateDegreesAbout(1, 2, 3, 45); -} -static void make_p(SkMatrix44* mat) { - SkScalar data[] = { - 1, 2, 3, 4, 5, 6, 7, 8, - 1, 2, 3, 4, 5, 6, 7, 8, - }; - mat->setRowMajor(data); -} - -typedef void (*Make44Proc)(SkMatrix44*); - -static const Make44Proc gMakeProcs[] = { - make_i, make_t, make_s, make_st, make_a, make_p -}; - -static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) { - SkScalar src2[] = { 1, 2 }; - SkScalar src4[] = { src2[0], src2[1], 0, 1 }; - SkScalar dstA[4], dstB[4]; - - for (int i = 0; i < 4; ++i) { - dstA[i] = SkScalar(123456789); - dstB[i] = SkScalar(987654321); - } - - mat.map2(src2, 1, dstA); - mat.mapScalars(src4, dstB); - - for (int i = 0; i < 4; ++i) { - REPORTER_ASSERT(reporter, dstA[i] == dstB[i]); - } -} - -static void test_map2(skiatest::Reporter* reporter) { - SkMatrix44 mat; - - for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) { - gMakeProcs[i](&mat); - test_map2(reporter, mat); - } -} - -static void test_gettype(skiatest::Reporter* reporter) { - SkMatrix44 matrix(SkMatrix44::kIdentity_Constructor); - - REPORTER_ASSERT(reporter, matrix.isIdentity()); - REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType()); - - int expectedMask; - - matrix.set(1, 1, 0); - expectedMask = SkMatrix44::kScale_Mask; - REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); - - matrix.set(0, 3, 1); // translate-x - expectedMask |= SkMatrix44::kTranslate_Mask; - REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); - - matrix.set(2, 0, 1); - expectedMask |= SkMatrix44::kAffine_Mask; - REPORTER_ASSERT(reporter, matrix.getType() == expectedMask); - - matrix.set(3, 2, 1); - REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask); - - // ensure that negative zero is treated as zero - SkScalar dx = 0; - SkScalar dy = 0; - SkScalar dz = 0; - matrix.setTranslate(-dx, -dy, -dz); - REPORTER_ASSERT(reporter, matrix.isIdentity()); - matrix.preTranslate(-dx, -dy, -dz); - REPORTER_ASSERT(reporter, matrix.isIdentity()); - matrix.postTranslate(-dx, -dy, -dz); - REPORTER_ASSERT(reporter, matrix.isIdentity()); -} - -static void test_common_angles(skiatest::Reporter* reporter) { - SkMatrix44 rot; - // Test precision of rotation in common cases - int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 }; - for (int i = 0; i < 9; ++i) { - rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i])); - - SkMatrix rot3x3 = SkMatrix(rot); - REPORTER_ASSERT(reporter, rot3x3.rectStaysRect()); - } -} - -static void test_concat(skiatest::Reporter* reporter) { - int i; - SkMatrix44 a,b,c,d; - - a.setTranslate(10, 10, 10); - b.setScale(2, 2, 2); - - SkScalar src[8] = { - 0, 0, 0, 1, - 1, 1, 1, 1 - }; - SkScalar dst[8]; - - c.setConcat(a, b); - - d = a; - d.preConcat(b); - REPORTER_ASSERT(reporter, d == c); - - c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); - for (i = 0; i < 3; ++i) { - REPORTER_ASSERT(reporter, 10 == dst[i]); - REPORTER_ASSERT(reporter, 12 == dst[i + 4]); - } - - c.setConcat(b, a); - - d = a; - d.postConcat(b); - REPORTER_ASSERT(reporter, d == c); - - c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4); - for (i = 0; i < 3; ++i) { - REPORTER_ASSERT(reporter, 20 == dst[i]); - REPORTER_ASSERT(reporter, 22 == dst[i + 4]); - } -} - -static void test_determinant(skiatest::Reporter* reporter) { - SkMatrix44 a(SkMatrix44::kIdentity_Constructor); - REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant())); - a.set(1, 1, 2); - REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant())); - SkMatrix44 b; - REPORTER_ASSERT(reporter, a.invert(&b)); - REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant())); - SkMatrix44 c = b = a; - c.set(0, 1, 4); - b.set(1, 0, 4); - REPORTER_ASSERT(reporter, - nearly_equal_double(a.determinant(), - b.determinant())); - SkMatrix44 d = a; - d.set(0, 0, 8); - REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant())); - - SkMatrix44 e = a; - e.postConcat(d); - REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant())); - e.set(0, 0, 0); - REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant())); -} - -static void test_invert(skiatest::Reporter* reporter) { - SkMatrix44 inverse; - double inverseData[16]; - - SkMatrix44 identity(SkMatrix44::kIdentity_Constructor); - identity.invert(&inverse); - inverse.asRowMajord(inverseData); - assert16(reporter, inverseData, - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1); - - SkMatrix44 translation; - translation.setTranslate(2, 3, 4); - translation.invert(&inverse); - inverse.asRowMajord(inverseData); - assert16(reporter, inverseData, - 1, 0, 0, -2, - 0, 1, 0, -3, - 0, 0, 1, -4, - 0, 0, 0, 1); - - SkMatrix44 scale; - scale.setScale(2, 4, 8); - scale.invert(&inverse); - inverse.asRowMajord(inverseData); - assert16(reporter, inverseData, - 0.5, 0, 0, 0, - 0, 0.25, 0, 0, - 0, 0, 0.125, 0, - 0, 0, 0, 1); - - SkMatrix44 scaleTranslation; - scaleTranslation.setScale(32, 128, 1024); - scaleTranslation.preTranslate(2, 3, 4); - scaleTranslation.invert(&inverse); - inverse.asRowMajord(inverseData); - assert16(reporter, inverseData, - 0.03125, 0, 0, -2, - 0, 0.0078125, 0, -3, - 0, 0, 0.0009765625, -4, - 0, 0, 0, 1); - - SkMatrix44 rotation; - rotation.setRotateDegreesAbout(0, 0, 1, 90); - rotation.invert(&inverse); - SkMatrix44 expected; - double expectedInverseRotation[16] = - {0, 1, 0, 0, - -1, 0, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1}; - expected.setRowMajord(expectedInverseRotation); - REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); - - SkMatrix44 affine; - affine.setRotateDegreesAbout(0, 0, 1, 90); - affine.preScale(10, 20, 100); - affine.preTranslate(2, 3, 4); - affine.invert(&inverse); - double expectedInverseAffine[16] = - {0, 0.1, 0, -2, - -0.05, 0, 0, -3, - 0, 0, 0.01, -4, - 0, 0, 0, 1}; - expected.setRowMajord(expectedInverseAffine); - REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); - - SkMatrix44 perspective(SkMatrix44::kIdentity_Constructor); - perspective.setDouble(3, 2, 1.0); - perspective.invert(&inverse); - double expectedInversePerspective[16] = - {1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, -1, 1}; - expected.setRowMajord(expectedInversePerspective); - REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); - - SkMatrix44 affineAndPerspective(SkMatrix44::kIdentity_Constructor); - affineAndPerspective.setDouble(3, 2, 1.0); - affineAndPerspective.preScale(10, 20, 100); - affineAndPerspective.preTranslate(2, 3, 4); - affineAndPerspective.invert(&inverse); - double expectedInverseAffineAndPerspective[16] = - {0.1, 0, 2, -2, - 0, 0.05, 3, -3, - 0, 0, 4.01, -4, - 0, 0, -1, 1}; - expected.setRowMajord(expectedInverseAffineAndPerspective); - REPORTER_ASSERT(reporter, nearly_equal(expected, inverse)); - - SkMatrix44 tinyScale(SkMatrix44::kIdentity_Constructor); - tinyScale.setDouble(0, 0, 1e-39); - REPORTER_ASSERT(reporter, tinyScale.getType() == SkMatrix44::kScale_Mask); - REPORTER_ASSERT(reporter, !tinyScale.invert(nullptr)); - REPORTER_ASSERT(reporter, !tinyScale.invert(&inverse)); - - SkMatrix44 tinyScaleTranslate(SkMatrix44::kIdentity_Constructor); - tinyScaleTranslate.setDouble(0, 0, 1e-38); - REPORTER_ASSERT(reporter, tinyScaleTranslate.invert(nullptr)); - tinyScaleTranslate.setDouble(0, 3, 10); - REPORTER_ASSERT( - reporter, tinyScaleTranslate.getType() == - (SkMatrix44::kScale_Mask | SkMatrix44::kTranslate_Mask)); - REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(nullptr)); - REPORTER_ASSERT(reporter, !tinyScaleTranslate.invert(&inverse)); - - SkMatrix44 tinyScalePerspective(SkMatrix44::kIdentity_Constructor); - tinyScalePerspective.setDouble(0, 0, 1e-39); - tinyScalePerspective.setDouble(3, 2, -1); - REPORTER_ASSERT(reporter, (tinyScalePerspective.getType() & - SkMatrix44::kPerspective_Mask) == - SkMatrix44::kPerspective_Mask); - REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(nullptr)); - REPORTER_ASSERT(reporter, !tinyScalePerspective.invert(&inverse)); -} - -static void test_transpose(skiatest::Reporter* reporter) { - SkMatrix44 a,b; - - int i = 0; - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - a.setDouble(row, col, i); - b.setDouble(col, row, i++); - } - } - - a.transpose(); - REPORTER_ASSERT(reporter, nearly_equal(a, b)); -} - -static void test_get_set_double(skiatest::Reporter* reporter) { - SkMatrix44 a; - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - a.setDouble(row, col, 3.141592653589793); - REPORTER_ASSERT(reporter, - nearly_equal_double(3.141592653589793, - a.getDouble(row, col))); - a.setDouble(row, col, 0); - REPORTER_ASSERT(reporter, - nearly_equal_double(0, a.getDouble(row, col))); - } - } -} - -static void test_set_3x3(skiatest::Reporter* r) { - static float vals[9] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, }; - - SkMatrix44 mat; - mat.set3x3RowMajorf(vals); - - REPORTER_ASSERT(r, 1.0f == mat.getFloat(0, 0)); - REPORTER_ASSERT(r, 2.0f == mat.getFloat(0, 1)); - REPORTER_ASSERT(r, 3.0f == mat.getFloat(0, 2)); - REPORTER_ASSERT(r, 4.0f == mat.getFloat(1, 0)); - REPORTER_ASSERT(r, 5.0f == mat.getFloat(1, 1)); - REPORTER_ASSERT(r, 6.0f == mat.getFloat(1, 2)); - REPORTER_ASSERT(r, 7.0f == mat.getFloat(2, 0)); - REPORTER_ASSERT(r, 8.0f == mat.getFloat(2, 1)); - REPORTER_ASSERT(r, 9.0f == mat.getFloat(2, 2)); -} - -static void test_set_row_col_major(skiatest::Reporter* reporter) { - SkMatrix44 a,b; - - for (int row = 0; row < 4; ++row) { - for (int col = 0; col < 4; ++col) { - a.setDouble(row, col, row * 4 + col); - } - } - - double bufferd[16]; - float bufferf[16]; - a.asColMajord(bufferd); - b.setColMajord(bufferd); - REPORTER_ASSERT(reporter, nearly_equal(a, b)); - b.setRowMajord(bufferd); - b.transpose(); - REPORTER_ASSERT(reporter, nearly_equal(a, b)); - a.asColMajorf(bufferf); - b.setColMajorf(bufferf); - REPORTER_ASSERT(reporter, nearly_equal(a, b)); - b.setRowMajorf(bufferf); - b.transpose(); - REPORTER_ASSERT(reporter, nearly_equal(a, b)); -} - -static void test_3x3_conversion(skiatest::Reporter* reporter) { - SkScalar values4x4[16] = { 1, 2, 3, 4, - 5, 6, 7, 8, - 9, 10, 11, 12, - 13, 14, 15, 16 }; - SkScalar values3x3[9] = { 1, 2, 4, - 5, 6, 8, - 13, 14, 16 }; - SkScalar values4x4flattened[16] = { 1, 2, 0, 4, - 5, 6, 0, 8, - 0, 0, 1, 0, - 13, 14, 0, 16 }; - SkMatrix44 a44; - a44.setRowMajor(values4x4); - - SkMatrix a33 = SkMatrix(a44); - SkMatrix expected33; - for (int i = 0; i < 9; i++) expected33[i] = values3x3[i]; - REPORTER_ASSERT(reporter, expected33 == a33); - - SkMatrix44 a44flattened = a33; - SkMatrix44 expected44flattened; - expected44flattened.setRowMajor(values4x4flattened); - REPORTER_ASSERT(reporter, nearly_equal(a44flattened, expected44flattened)); - - // Test that a point with a Z value of 0 is transformed the same way. - SkScalar vec4[4] = { 2, 4, 0, 8 }; - SkPoint3 vec3 = { 2, 4, 8 }; - - SkScalar vec4transformed[4]; - SkPoint3 vec3transformed; - SkScalar vec4transformed2[4]; - a44.mapScalars(vec4, vec4transformed); - a33.mapHomogeneousPoints(&vec3transformed, &vec3, 1); - a44flattened.mapScalars(vec4, vec4transformed2); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec3transformed.fX)); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec3transformed.fY)); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec3transformed.fZ)); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[0], vec4transformed2[0])); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[1], vec4transformed2[1])); - REPORTER_ASSERT(reporter, !nearly_equal_scalar(vec4transformed[2], vec4transformed2[2])); - REPORTER_ASSERT(reporter, nearly_equal_scalar(vec4transformed[3], vec4transformed2[3])); -} - -static void test_has_perspective(skiatest::Reporter* reporter) { - SkMatrix44 transform(SkMatrix44::kIdentity_Constructor); - - transform.setDouble(3, 2, -0.1); - REPORTER_ASSERT(reporter, transform.hasPerspective()); - - transform.reset(); - REPORTER_ASSERT(reporter, !transform.hasPerspective()); - - transform.setDouble(3, 0, -1.0); - REPORTER_ASSERT(reporter, transform.hasPerspective()); - - transform.reset(); - transform.setDouble(3, 1, -1.0); - REPORTER_ASSERT(reporter, transform.hasPerspective()); - - transform.reset(); - transform.setDouble(3, 2, -0.3); - REPORTER_ASSERT(reporter, transform.hasPerspective()); - - transform.reset(); - transform.setDouble(3, 3, 0.5); - REPORTER_ASSERT(reporter, transform.hasPerspective()); - - transform.reset(); - transform.setDouble(3, 3, 0.0); - REPORTER_ASSERT(reporter, transform.hasPerspective()); -} - -static bool is_rectilinear (SkVector4& p1, SkVector4& p2, SkVector4& p3, SkVector4& p4) { - return (SkScalarNearlyEqual(p1.fData[0], p2.fData[0]) && - SkScalarNearlyEqual(p2.fData[1], p3.fData[1]) && - SkScalarNearlyEqual(p3.fData[0], p4.fData[0]) && - SkScalarNearlyEqual(p4.fData[1], p1.fData[1])) || - (SkScalarNearlyEqual(p1.fData[1], p2.fData[1]) && - SkScalarNearlyEqual(p2.fData[0], p3.fData[0]) && - SkScalarNearlyEqual(p3.fData[1], p4.fData[1]) && - SkScalarNearlyEqual(p4.fData[0], p1.fData[0])); -} - -static SkVector4 mul_with_persp_divide(const SkMatrix44& transform, const SkVector4& target) { - SkVector4 result = transform * target; - if (result.fData[3] != 0.0f && result.fData[3] != SK_Scalar1) { - float wInverse = SK_Scalar1 / result.fData[3]; - result.set(result.fData[0] * wInverse, - result.fData[1] * wInverse, - result.fData[2] * wInverse, - SK_Scalar1); - } - return result; -} - -static bool empirically_preserves_2d_axis_alignment(skiatest::Reporter* reporter, - const SkMatrix44& transform) { - SkVector4 p1(5.0f, 5.0f, 0.0f); - SkVector4 p2(10.0f, 5.0f, 0.0f); - SkVector4 p3(10.0f, 20.0f, 0.0f); - SkVector4 p4(5.0f, 20.0f, 0.0f); - - REPORTER_ASSERT(reporter, is_rectilinear(p1, p2, p3, p4)); - - p1 = mul_with_persp_divide(transform, p1); - p2 = mul_with_persp_divide(transform, p2); - p3 = mul_with_persp_divide(transform, p3); - p4 = mul_with_persp_divide(transform, p4); - - return is_rectilinear(p1, p2, p3, p4); -} - -static void test(bool expected, skiatest::Reporter* reporter, const SkMatrix44& transform) { - if (expected) { - REPORTER_ASSERT(reporter, empirically_preserves_2d_axis_alignment(reporter, transform)); - REPORTER_ASSERT(reporter, transform.preserves2dAxisAlignment()); - } else { - REPORTER_ASSERT(reporter, !empirically_preserves_2d_axis_alignment(reporter, transform)); - REPORTER_ASSERT(reporter, !transform.preserves2dAxisAlignment()); - } -} - -static void test_preserves_2d_axis_alignment(skiatest::Reporter* reporter) { - SkMatrix44 transform; - SkMatrix44 transform2; - - static const struct TestCase { - SkScalar a; // row 1, column 1 - SkScalar b; // row 1, column 2 - SkScalar c; // row 2, column 1 - SkScalar d; // row 2, column 2 - bool expected; - } test_cases[] = { - { 3.f, 0.f, - 0.f, 4.f, true }, // basic case - { 0.f, 4.f, - 3.f, 0.f, true }, // rotate by 90 - { 0.f, 0.f, - 0.f, 4.f, true }, // degenerate x - { 3.f, 0.f, - 0.f, 0.f, true }, // degenerate y - { 0.f, 0.f, - 3.f, 0.f, true }, // degenerate x + rotate by 90 - { 0.f, 4.f, - 0.f, 0.f, true }, // degenerate y + rotate by 90 - { 3.f, 4.f, - 0.f, 0.f, false }, - { 0.f, 0.f, - 3.f, 4.f, false }, - { 0.f, 3.f, - 0.f, 4.f, false }, - { 3.f, 0.f, - 4.f, 0.f, false }, - { 3.f, 4.f, - 5.f, 0.f, false }, - { 3.f, 4.f, - 0.f, 5.f, false }, - { 3.f, 0.f, - 4.f, 5.f, false }, - { 0.f, 3.f, - 4.f, 5.f, false }, - { 2.f, 3.f, - 4.f, 5.f, false }, - }; - - for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { - const TestCase& value = test_cases[i]; - transform.setIdentity(); - transform.set(0, 0, value.a); - transform.set(0, 1, value.b); - transform.set(1, 0, value.c); - transform.set(1, 1, value.d); - - test(value.expected, reporter, transform); - } - - // Try the same test cases again, but this time make sure that other matrix - // elements (except perspective) have entries, to test that they are ignored. - for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { - const TestCase& value = test_cases[i]; - transform.setIdentity(); - transform.set(0, 0, value.a); - transform.set(0, 1, value.b); - transform.set(1, 0, value.c); - transform.set(1, 1, value.d); - - transform.set(0, 2, 1.f); - transform.set(0, 3, 2.f); - transform.set(1, 2, 3.f); - transform.set(1, 3, 4.f); - transform.set(2, 0, 5.f); - transform.set(2, 1, 6.f); - transform.set(2, 2, 7.f); - transform.set(2, 3, 8.f); - - test(value.expected, reporter, transform); - } - - // Try the same test cases again, but this time add perspective which is - // always assumed to not-preserve axis alignment. - for (size_t i = 0; i < sizeof(test_cases)/sizeof(TestCase); ++i) { - const TestCase& value = test_cases[i]; - transform.setIdentity(); - transform.set(0, 0, value.a); - transform.set(0, 1, value.b); - transform.set(1, 0, value.c); - transform.set(1, 1, value.d); - - transform.set(0, 2, 1.f); - transform.set(0, 3, 2.f); - transform.set(1, 2, 3.f); - transform.set(1, 3, 4.f); - transform.set(2, 0, 5.f); - transform.set(2, 1, 6.f); - transform.set(2, 2, 7.f); - transform.set(2, 3, 8.f); - transform.set(3, 0, 9.f); - transform.set(3, 1, 10.f); - transform.set(3, 2, 11.f); - transform.set(3, 3, 12.f); - - test(false, reporter, transform); - } - - // Try a few more practical situations to check precision - // Reuse TestCase (a, b, c, d) as (x, y, z, degrees) axis to rotate about. - TestCase rotation_tests[] = { - { 0.0, 0.0, 1.0, 90.0, true }, - { 0.0, 0.0, 1.0, 180.0, true }, - { 0.0, 0.0, 1.0, 270.0, true }, - { 0.0, 1.0, 0.0, 90.0, true }, - { 1.0, 0.0, 0.0, 90.0, true }, - { 0.0, 0.0, 1.0, 45.0, false }, - // In 3d these next two are non-preserving, but we're testing in 2d after - // orthographic projection, where they are. - { 0.0, 1.0, 0.0, 45.0, true }, - { 1.0, 0.0, 0.0, 45.0, true }, - }; - - for (size_t i = 0; i < sizeof(rotation_tests)/sizeof(TestCase); ++i) { - const TestCase& value = rotation_tests[i]; - transform.setRotateDegreesAbout(value.a, value.b, value.c, value.d); - test(value.expected, reporter, transform); - } - - static const struct DoubleRotationCase { - SkScalar x1; - SkScalar y1; - SkScalar z1; - SkScalar degrees1; - SkScalar x2; - SkScalar y2; - SkScalar z2; - SkScalar degrees2; - bool expected; - } double_rotation_tests[] = { - { 0.0, 0.0, 1.0, 90.0, 0.0, 1.0, 0.0, 90.0, true }, - { 0.0, 0.0, 1.0, 90.0, 1.0, 0.0, 0.0, 90.0, true }, - { 0.0, 1.0, 0.0, 90.0, 0.0, 0.0, 1.0, 90.0, true }, - }; - - for (size_t i = 0; i < sizeof(double_rotation_tests)/sizeof(DoubleRotationCase); ++i) { - const DoubleRotationCase& value = double_rotation_tests[i]; - transform.setRotateDegreesAbout(value.x1, value.y1, value.z1, value.degrees1); - transform2.setRotateDegreesAbout(value.x2, value.y2, value.z2, value.degrees2); - transform.postConcat(transform2); - test(value.expected, reporter, transform); - } - - // Perspective cases. - transform.setIdentity(); - transform.setDouble(3, 2, -0.1); // Perspective depth 10 - transform2.setRotateDegreesAbout(0.0, 1.0, 0.0, 45.0); - transform.preConcat(transform2); - test(false, reporter, transform); - - transform.setIdentity(); - transform.setDouble(3, 2, -0.1); // Perspective depth 10 - transform2.setRotateDegreesAbout(0.0, 0.0, 1.0, 90.0); - transform.preConcat(transform2); - test(true, reporter, transform); -} - -// just want to exercise the various converters for Scalar -static void test_toint(skiatest::Reporter* reporter) { - SkMatrix44 mat; - mat.setScale(3, 3, 3); - - SkScalar sum = SkScalarFloorToScalar(mat.get(0, 0)) + - SkScalarRoundToScalar(mat.get(1, 0)) + - SkScalarCeilToScalar(mat.get(2, 0)); - int isum = SkScalarFloorToInt(mat.get(0, 1)) + - SkScalarRoundToInt(mat.get(1, 2)) + - SkScalarCeilToInt(mat.get(2, 3)); - REPORTER_ASSERT(reporter, sum >= 0); - REPORTER_ASSERT(reporter, isum >= 0); - REPORTER_ASSERT(reporter, static_cast(isum) == SkIntToScalar(isum)); -} - -DEF_TEST(Matrix44, reporter) { - SkMatrix44 mat; - SkMatrix44 inverse; - SkMatrix44 iden1; - SkMatrix44 iden2; - SkMatrix44 rot; - - mat.setTranslate(1, 1, 1); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - - mat.setScale(2, 2, 2); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - - mat.setScale(SK_Scalar1/2, SK_Scalar1/2, SK_Scalar1/2); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - - mat.setScale(3, 3, 3); - rot.setRotateDegreesAbout(0, 0, -1, 90); - mat.postConcat(rot); - REPORTER_ASSERT(reporter, mat.invert(nullptr)); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - iden2.setConcat(inverse, mat); - REPORTER_ASSERT(reporter, is_identity(iden2)); - - // test tiny-valued matrix inverse - mat.reset(); - auto v = 1.0e-12f; - mat.setScale(v,v,v); - rot.setRotateDegreesAbout(0, 0, -1, 90); - mat.postConcat(rot); - mat.postTranslate(v,v,v); - REPORTER_ASSERT(reporter, mat.invert(nullptr)); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - - // test mixed-valued matrix inverse - mat.reset(); - mat.setScale(1.0e-2f, 3.0f, 1.0e+2f); - rot.setRotateDegreesAbout(0, 0, -1, 90); - mat.postConcat(rot); - mat.postTranslate(1.0e+2f, 3.0f, 1.0e-2f); - REPORTER_ASSERT(reporter, mat.invert(nullptr)); - mat.invert(&inverse); - iden1.setConcat(mat, inverse); - REPORTER_ASSERT(reporter, is_identity(iden1)); - - // test degenerate matrix - mat.reset(); - mat.set3x3(1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 0.0); - REPORTER_ASSERT(reporter, !mat.invert(nullptr)); - - // test rol/col Major getters - { - mat.setTranslate(2, 3, 4); - float dataf[16]; - double datad[16]; - - mat.asColMajorf(dataf); - assert16(reporter, dataf, - 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 2, 3, 4, 1); - mat.asColMajord(datad); - assert16(reporter, datad, 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 2, 3, 4, 1); - mat.asRowMajorf(dataf); - assert16(reporter, dataf, 1, 0, 0, 2, - 0, 1, 0, 3, - 0, 0, 1, 4, - 0, 0, 0, 1); - mat.asRowMajord(datad); - assert16(reporter, datad, 1, 0, 0, 2, - 0, 1, 0, 3, - 0, 0, 1, 4, - 0, 0, 0, 1); - } - - test_concat(reporter); - - if (false) { // avoid bit rot, suppress warning (working on making this pass) - test_common_angles(reporter); - } - - test_constructor(reporter); - test_gettype(reporter); - test_determinant(reporter); - test_invert(reporter); - test_transpose(reporter); - test_get_set_double(reporter); - test_set_row_col_major(reporter); - test_set_3x3(reporter); - test_translate(reporter); - test_scale(reporter); - test_map2(reporter); - test_3x3_conversion(reporter); - test_has_perspective(reporter); - test_preserves_2d_axis_alignment(reporter); - test_toint(reporter); -}