aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--bench/Matrix44Bench.cpp140
-rw-r--r--gyp/bench.gypi1
-rw-r--r--include/core/SkPostConfig.h4
-rw-r--r--include/utils/SkMatrix44.h147
-rw-r--r--src/utils/SkMatrix44.cpp192
-rw-r--r--tests/Matrix44Test.cpp61
6 files changed, 451 insertions, 94 deletions
diff --git a/bench/Matrix44Bench.cpp b/bench/Matrix44Bench.cpp
new file mode 100644
index 0000000000..f10870c143
--- /dev/null
+++ b/bench/Matrix44Bench.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkMatrix44.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class Matrix44Bench : public SkBenchmark {
+ SkString fName;
+ enum { N = 10000 };
+public:
+ Matrix44Bench(void* param, const char name[]) : INHERITED(param) {
+ fName.printf("matrix44_%s", name);
+ fIsRendering = false;
+ }
+
+ virtual void performTest() = 0;
+
+protected:
+ virtual int mulLoopCount() const { return 1; }
+
+ virtual const char* onGetName() {
+ return fName.c_str();
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ int n = SkBENCHLOOP(N * this->mulLoopCount());
+ for (int i = 0; i < n; i++) {
+ this->performTest();
+ }
+ }
+
+private:
+ typedef SkBenchmark INHERITED;
+};
+
+class EqualsMatrix44Bench : public Matrix44Bench {
+public:
+ EqualsMatrix44Bench(void* param) : INHERITED(param, "equals") {
+ fM1.set(0, 0, 0);
+ fM2.set(3, 3, 0);
+ }
+protected:
+ virtual void performTest() {
+ for (int i = 0; i < 10; ++i) {
+ fM0 == fM1;
+ fM1 == fM2;
+ fM2 == fM0;
+ }
+ }
+private:
+ SkMatrix44 fM0, fM1, fM2;
+ typedef Matrix44Bench INHERITED;
+};
+
+class PreScaleMatrix44Bench : public Matrix44Bench {
+public:
+ PreScaleMatrix44Bench(void* param) : INHERITED(param, "prescale") {
+ fX = fY = fZ = SkDoubleToMScalar(1.5);
+ }
+protected:
+ virtual void performTest() {
+ fM0.reset();
+ for (int i = 0; i < 10; ++i) {
+ fM0.preScale(fX, fY, fZ);
+ }
+ }
+private:
+ SkMatrix44 fM0;
+ SkMScalar fX, fY, fZ;
+ typedef Matrix44Bench INHERITED;
+};
+
+class PostScaleMatrix44Bench : public Matrix44Bench {
+public:
+ PostScaleMatrix44Bench(void* param) : INHERITED(param, "postscale") {
+ fX = fY = fZ = SkDoubleToMScalar(1.5);
+ }
+protected:
+ virtual void performTest() {
+ fM0.reset();
+ for (int i = 0; i < 10; ++i) {
+ fM0.postScale(fX, fY, fZ);
+ }
+ }
+private:
+ SkMatrix44 fM0;
+ SkMScalar fX, fY, fZ;
+ typedef Matrix44Bench INHERITED;
+};
+
+class SetConcatMatrix44Bench : public Matrix44Bench {
+public:
+ SetConcatMatrix44Bench(void* param) : INHERITED(param, "setconcat") {
+ fX = fY = fZ = SkDoubleToMScalar(1.5);
+ fM1.setScale(fX, fY, fZ);
+ fM2.setTranslate(fX, fY, fZ);
+ }
+protected:
+ virtual void performTest() {
+ fM0.reset(); // just to normalize this test with prescale/postscale
+ for (int i = 0; i < 10; ++i) {
+ fM0.setConcat(fM1, fM2);
+ }
+ }
+private:
+ SkMatrix44 fM0, fM1, fM2;
+ SkMScalar fX, fY, fZ;
+ typedef Matrix44Bench INHERITED;
+};
+
+class GetTypeMatrix44Bench : public Matrix44Bench {
+public:
+ GetTypeMatrix44Bench(void* param) : INHERITED(param, "gettype") {}
+protected:
+ // Putting random generation of the matrix inside performTest()
+ // would help us avoid anomalous runs, but takes up 25% or
+ // more of the function time.
+ virtual void performTest() {
+ for (int i = 0; i < 20; ++i) {
+ fMatrix.set(1, 2, 1); // to invalidate the type-cache
+ fMatrix.getType();
+ }
+ }
+private:
+ SkMatrix44 fMatrix;
+ typedef Matrix44Bench INHERITED;
+};
+
+DEF_BENCH( return new EqualsMatrix44Bench(p); )
+DEF_BENCH( return new PreScaleMatrix44Bench(p); )
+DEF_BENCH( return new PostScaleMatrix44Bench(p); )
+DEF_BENCH( return new SetConcatMatrix44Bench(p); )
+DEF_BENCH( return new GetTypeMatrix44Bench(p); )
+
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index bc90c5ea94..dbb30b6fff 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -21,6 +21,7 @@
'../bench/InterpBench.cpp',
'../bench/LineBench.cpp',
'../bench/MathBench.cpp',
+ '../bench/Matrix44Bench.cpp',
'../bench/MatrixBench.cpp',
'../bench/MatrixConvolutionBench.cpp',
'../bench/MemoryBench.cpp',
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index 9a5c54a171..12fe87dafe 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -33,7 +33,9 @@
#if defined(SK_MSCALAR_IS_DOUBLE) && defined(SK_MSCALAR_IS_FLOAT)
#error "cannot define both SK_MSCALAR_IS_DOUBLE and SK_MSCALAR_IS_FLOAT"
#elif !defined(SK_MSCALAR_IS_DOUBLE) && !defined(SK_MSCALAR_IS_FLOAT)
- #define SK_MSCALAR_IS_FLOAT
+ // default is double, as that is faster given our impl uses doubles
+ // for intermediate calculations.
+ #define SK_MSCALAR_IS_DOUBLE
#endif
#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
diff --git a/include/utils/SkMatrix44.h b/include/utils/SkMatrix44.h
index 67486b38a1..79bc700d14 100644
--- a/include/utils/SkMatrix44.h
+++ b/include/utils/SkMatrix44.h
@@ -1,4 +1,3 @@
-
/*
* Copyright 2011 Google Inc.
*
@@ -6,8 +5,6 @@
* found in the LICENSE file.
*/
-
-
#ifndef SkMatrix44_DEFINED
#define SkMatrix44_DEFINED
@@ -15,7 +12,12 @@
#include "SkScalar.h"
#ifdef SK_MSCALAR_IS_DOUBLE
+#ifdef SK_MSCALAR_IS_FLOAT
+ #error "can't define MSCALAR both as DOUBLE and FLOAT"
+#endif
typedef double SkMScalar;
+ typedef int64_t SkMIntScalar;
+
static inline double SkFloatToMScalar(float x) {
return static_cast<double>(x);
}
@@ -30,7 +32,12 @@
}
static const SkMScalar SK_MScalarPI = 3.141592653589793;
#elif defined SK_MSCALAR_IS_FLOAT
+#ifdef SK_MSCALAR_IS_DOUBLE
+ #error "can't define MSCALAR both as DOUBLE and FLOAT"
+#endif
typedef float SkMScalar;
+ typedef int32_t SkMIntScalar;
+
static inline float SkFloatToMScalar(float x) {
return x;
}
@@ -96,11 +103,12 @@ struct SkVector4 {
class SK_API SkMatrix44 {
public:
- SkMatrix44();
+ SkMatrix44() { this->setIdentity(); }
SkMatrix44(const SkMatrix44&);
SkMatrix44(const SkMatrix44& a, const SkMatrix44& b);
SkMatrix44& operator=(const SkMatrix44& src) {
+ SkASSERT(sizeof(src) == sizeof(fMat) + sizeof(SkMIntScalar));
memcpy(this, &src, sizeof(*this));
return *this;
}
@@ -114,23 +122,70 @@ public:
SkMatrix44& operator=(const SkMatrix& src);
operator SkMatrix() const;
- SkMScalar get(int row, int col) const {
+ /**
+ * Return a reference to a const identity matrix
+ */
+ static const SkMatrix44& I();
+
+ enum TypeMask {
+ kIdentity_Mask = 0,
+ kTranslate_Mask = 0x01, //!< set if the matrix has translation
+ kScale_Mask = 0x02, //!< set if the matrix has any scale != 1
+ kAffine_Mask = 0x04, //!< set if the matrix skews or rotates
+ kPerspective_Mask = 0x08 //!< 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 {
+ if (fTypeMask & kUnknown_Mask) {
+ fTypeMask = this->computeTypeMask();
+ }
+ SkASSERT(!(fTypeMask & kUnknown_Mask));
+ return (TypeMask)fTypeMask;
+ }
+
+ inline bool isIdentity() const {
+ return 0 == this->getType();
+ }
+
+ 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 SkMScalar get(int row, int col) const {
SkASSERT((unsigned)row <= 3);
SkASSERT((unsigned)col <= 3);
return fMat[col][row];
}
- void set(int row, int col, SkMScalar value) {
+ /**
+ * 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, SkMScalar value) {
SkASSERT((unsigned)row <= 3);
SkASSERT((unsigned)col <= 3);
fMat[col][row] = value;
- fIdentity = false;
+ this->dirtyTypeMask();
}
- double getDouble(int row, int col) const {
+ inline double getDouble(int row, int col) const {
return SkMScalarToDouble(this->get(row, col));
}
- void setDouble(int row, int col, double value) {
+ inline void setDouble(int row, int col, double value) {
this->set(row, col, SkDoubleToMScalar(value));
}
@@ -154,9 +209,13 @@ public:
void setRowMajorf(const float[]);
void setRowMajord(const double[]);
- bool isIdentity() const;
- void setIdentity();
- void reset() { this->setIdentity();}
+#ifdef SK_MSCALAR_IS_FLOAT
+ void setColMajor(const SkMScalar data[]) { this->setColMajorf(data); }
+ void setRowMajor(const SkMScalar data[]) { this->setRowMajorf(data); }
+#else
+ void setColMajor(const SkMScalar data[]) { this->setColMajord(data); }
+ void setRowMajor(const SkMScalar data[]) { this->setRowMajord(data); }
+#endif
void set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
SkMScalar m10, SkMScalar m11, SkMScalar m12,
@@ -170,13 +229,13 @@ public:
void preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
void postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
- void setScale(SkMScalar scale) {
+ inline void setScale(SkMScalar scale) {
this->setScale(scale, scale, scale);
}
- void preScale(SkMScalar scale) {
+ inline void preScale(SkMScalar scale) {
this->preScale(scale, scale, scale);
}
- void postScale(SkMScalar scale) {
+ inline void postScale(SkMScalar scale) {
this->postScale(scale, scale, scale);
}
@@ -197,10 +256,10 @@ public:
SkMScalar radians);
void setConcat(const SkMatrix44& a, const SkMatrix44& b);
- void preConcat(const SkMatrix44& m) {
+ inline void preConcat(const SkMatrix44& m) {
this->setConcat(*this, m);
}
- void postConcat(const SkMatrix44& m) {
+ inline void postConcat(const SkMatrix44& m) {
this->setConcat(m, *this);
}
@@ -220,7 +279,7 @@ public:
It is legal for src and dst to point to the same memory.
*/
void mapScalars(const SkScalar src[4], SkScalar dst[4]) const;
- void mapScalars(SkScalar vec[4]) const {
+ inline void mapScalars(SkScalar vec[4]) const {
this->mapScalars(vec, vec);
}
@@ -236,11 +295,11 @@ public:
#ifdef SK_MSCALAR_IS_DOUBLE
void mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const;
#elif defined SK_MSCALAR_IS_FLOAT
- void mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
+ inline void mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
this->mapScalars(src, dst);
}
#endif
- void mapMScalars(SkMScalar vec[4]) const {
+ inline void mapMScalars(SkMScalar vec[4]) const {
this->mapMScalars(vec, vec);
}
@@ -255,14 +314,48 @@ public:
double determinant() const;
private:
- /* Stored in the same order as opengl:
- [3][0] = tx
- [3][1] = ty
- [3][2] = tz
- */
- SkMScalar fMat[4][4];
+ SkMScalar fMat[4][4];
+ // we use SkMIntScalar instead of just int, as we want to ensure that
+ // we are always packed with no extra bits, allowing us to call memcpy
+ // without fear of copying uninitialized bits.
+ mutable SkMIntScalar fTypeMask;
+
+ enum {
+ kUnknown_Mask = 0x80,
+
+ kAllPublic_Masks = 0xF
+ };
+
+ SkMScalar transX() const { return fMat[3][0]; }
+ SkMScalar transY() const { return fMat[3][1]; }
+ SkMScalar transZ() const { return fMat[3][2]; }
+
+ SkMScalar scaleX() const { return fMat[0][0]; }
+ SkMScalar scaleY() const { return fMat[1][1]; }
+ SkMScalar scaleZ() const { return fMat[2][2]; }
+
+ SkMScalar perspX() const { return fMat[0][3]; }
+ SkMScalar perspY() const { return fMat[1][3]; }
+ SkMScalar perspZ() const { return fMat[2][3]; }
- bool fIdentity;
+ int computeTypeMask() const;
+
+ inline void dirtyTypeMask() {
+ fTypeMask = kUnknown_Mask;
+ }
+
+ inline void setTypeMask(int mask) {
+ SkASSERT(0 == (~(kAllPublic_Masks | kUnknown_Mask) & mask));
+ fTypeMask = mask;
+ }
+
+ /**
+ * Does not take the time to 'compute' the typemask. Only returns true if
+ * we already know that this matrix is identity.
+ */
+ inline bool isTriviallyIdentity() const {
+ return 0 == fTypeMask;
+ }
};
#endif
diff --git a/src/utils/SkMatrix44.cpp b/src/utils/SkMatrix44.cpp
index 43caacd552..489a550ed7 100644
--- a/src/utils/SkMatrix44.cpp
+++ b/src/utils/SkMatrix44.cpp
@@ -10,10 +10,6 @@
#include "SkMatrix44.h"
-SkMatrix44::SkMatrix44() {
- this->setIdentity();
-}
-
SkMatrix44::SkMatrix44(const SkMatrix44& src) {
memcpy(this, &src, sizeof(src));
}
@@ -22,18 +18,69 @@ SkMatrix44::SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
this->setConcat(a, b);
}
+static inline bool eq4(const SkMScalar* SK_RESTRICT a,
+ const SkMScalar* 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 (fIdentity && other.fIdentity)
+ if (this == &other) {
+ return true;
+ }
+
+ if (this->isTriviallyIdentity() && other.isTriviallyIdentity()) {
return true;
+ }
+
+ const SkMScalar* SK_RESTRICT a = &fMat[0][0];
+ const SkMScalar* SK_RESTRICT b = &other.fMat[0][0];
- const SkMScalar* a = &fMat[0][0];
- const SkMScalar* 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
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkMatrix44::computeTypeMask() const {
+ unsigned mask = 0;
+
+ if (0 != perspX() || 0 != perspY() || 0 != perspZ() || 1 != fMat[3][3]) {
+ return kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_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;
+ }
+
+ return mask;
}
///////////////////////////////////////////////////////////////////////////////
@@ -93,7 +140,8 @@ void SkMatrix44::setColMajorf(const float src[]) {
#elif defined SK_MSCALAR_IS_FLOAT
memcpy(dst, src, 16 * sizeof(float));
#endif
- fIdentity = false;
+
+ this->dirtyTypeMask();
}
void SkMatrix44::setColMajord(const double src[]) {
@@ -105,7 +153,8 @@ void SkMatrix44::setColMajord(const double src[]) {
dst[i] = SkDoubleToMScalar(src[i]);
}
#endif
- fIdentity = false;
+
+ this->dirtyTypeMask();
}
void SkMatrix44::setRowMajorf(const float src[]) {
@@ -118,7 +167,7 @@ void SkMatrix44::setRowMajorf(const float src[]) {
src += 4;
dst += 1;
}
- fIdentity = false;
+ this->dirtyTypeMask();
}
void SkMatrix44::setRowMajord(const double src[]) {
@@ -131,41 +180,30 @@ void SkMatrix44::setRowMajord(const double src[]) {
src += 4;
dst += 1;
}
- fIdentity = false;
+ this->dirtyTypeMask();
}
///////////////////////////////////////////////////////////////////////////////
-bool SkMatrix44::isIdentity() const {
- if (fIdentity)
- return true;
-
- static const SkMScalar sIdentityMat[4][4] = {
- { 1, 0, 0, 0 },
- { 0, 1, 0, 0 },
- { 0, 0, 1, 0 },
- { 0, 0, 0, 1 },
- };
- return !memcmp(fMat, sIdentityMat, sizeof(fMat));
+const SkMatrix44& SkMatrix44::I() {
+ static SkMatrix44 gIdentity;
+ return gIdentity;
}
-///////////////////////////////////////////////////////////////////////////////
-
void SkMatrix44::setIdentity() {
sk_bzero(fMat, sizeof(fMat));
fMat[0][0] = fMat[1][1] = fMat[2][2] = fMat[3][3] = 1;
- fIdentity = true;
+ this->setTypeMask(kIdentity_Mask);
}
void SkMatrix44::set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
SkMScalar m10, SkMScalar m11, SkMScalar m12,
SkMScalar m20, SkMScalar m21, SkMScalar m22) {
- sk_bzero(fMat, sizeof(fMat));
fMat[0][0] = m00; fMat[0][1] = m01; fMat[0][2] = m02; fMat[0][3] = 0;
fMat[1][0] = m10; fMat[1][1] = m11; fMat[1][2] = m12; fMat[1][3] = 0;
fMat[2][0] = m20; fMat[2][1] = m21; fMat[2][2] = m22; fMat[2][3] = 0;
fMat[3][0] = 0; fMat[3][1] = 0; fMat[3][2] = 0; fMat[3][3] = 1;
- fIdentity = false;
+ this->dirtyTypeMask();
}
///////////////////////////////////////////////////////////////////////////////
@@ -176,7 +214,12 @@ void SkMatrix44::setTranslate(SkMScalar tx, SkMScalar ty, SkMScalar tz) {
fMat[3][1] = ty;
fMat[3][2] = tz;
fMat[3][3] = 1;
- fIdentity = false;
+
+ int mask = kIdentity_Mask;
+ if (0 != tx || 0 != ty || 0 != tz) {
+ mask |= kTranslate_Mask;
+ }
+ this->setTypeMask(mask);
}
void SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
@@ -189,7 +232,7 @@ void SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
fMat[3][0] += dx;
fMat[3][1] += dy;
fMat[3][2] += dz;
- fIdentity = false;
+ this->dirtyTypeMask();
}
///////////////////////////////////////////////////////////////////////////////
@@ -200,7 +243,12 @@ void SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
fMat[1][1] = sy;
fMat[2][2] = sz;
fMat[3][3] = 1;
- fIdentity = false;
+
+ int mask = kIdentity_Mask;
+ if (0 != sx || 0 != sy || 0 != sz) {
+ mask |= kScale_Mask;
+ }
+ this->setTypeMask(mask);
}
void SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
@@ -215,16 +263,16 @@ void SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
fMat[i][1] *= sy;
fMat[i][2] *= sz;
}
- fIdentity = false;
+ this->dirtyTypeMask();
}
///////////////////////////////////////////////////////////////////////////////
void SkMatrix44::setRotateAbout(SkMScalar x, SkMScalar y, SkMScalar z,
SkMScalar radians) {
- double len2 = x * x + y * y + z * z;
- if (len2 != 1) {
- if (len2 == 0) {
+ double len2 = (double)x * x + (double)y * y + (double)z * z;
+ if (1 != len2) {
+ if (0 == len2) {
this->setIdentity();
return;
}
@@ -268,18 +316,33 @@ void SkMatrix44::setRotateAboutUnit(SkMScalar x, SkMScalar y, SkMScalar z,
///////////////////////////////////////////////////////////////////////////////
void SkMatrix44::setConcat(const SkMatrix44& a, const SkMatrix44& b) {
- SkMScalar result[4][4];
- for (int i = 0; i < 4; i++) {
- for (int j = 0; j < 4; j++) {
+ if (a.isIdentity()) {
+ *this = b;
+ return;
+ }
+ if (b.isIdentity()) {
+ *this = a;
+ return;
+ }
+
+ bool useStorage = (this == &a || this == &b);
+ SkMScalar storage[16];
+ SkMScalar* result = useStorage ? storage : &fMat[0][0];
+
+ 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 += SkMScalarToDouble(a.fMat[k][i]) * b.fMat[j][k];
}
- result[j][i] = SkDoubleToMScalar(value);
+ *result++ = SkDoubleToMScalar(value);
}
}
- memcpy(fMat, result, sizeof(result));
- fIdentity = false;
+ if (useStorage) {
+ memcpy(fMat, storage, sizeof(storage));
+ }
+
+ this->dirtyTypeMask();
}
///////////////////////////////////////////////////////////////////////////////
@@ -328,10 +391,20 @@ static inline double dabs(double x) {
}
bool SkMatrix44::invert(SkMatrix44* inverse) const {
+ if (this->isTriviallyIdentity()) {
+ if (inverse) {
+ *inverse = *this;
+ return true;
+ }
+ }
+
double det = this->determinant();
if (dabs(det) < TOO_SMALL_FOR_DETERMINANT) {
return false;
}
+
+ // We now we will succeed, so return early if the caller doesn't actually
+ // want the computed inverse.
if (NULL == inverse) {
return true;
}
@@ -380,7 +453,7 @@ bool SkMatrix44::invert(SkMatrix44* inverse) const {
inverse->fMat[i][j] = SkDoubleToMScalar(tmp[i][j] * invDet);
}
}
- inverse->fIdentity = false;
+ inverse->dirtyTypeMask();
return true;
}
@@ -393,12 +466,18 @@ void SkMatrix44::transpose() {
SkTSwap(fMat[1][2], fMat[2][1]);
SkTSwap(fMat[1][3], fMat[3][1]);
SkTSwap(fMat[2][3], fMat[3][2]);
+
+ if (!this->isTriviallyIdentity()) {
+ this->dirtyTypeMask();
+ }
}
///////////////////////////////////////////////////////////////////////////////
void SkMatrix44::mapScalars(const SkScalar src[4], SkScalar dst[4]) const {
- SkScalar result[4];
+ SkScalar storage[4];
+ SkScalar* result = (src == dst) ? storage : dst;
+
for (int i = 0; i < 4; i++) {
SkMScalar value = 0;
for (int j = 0; j < 4; j++) {
@@ -406,21 +485,31 @@ void SkMatrix44::mapScalars(const SkScalar src[4], SkScalar dst[4]) const {
}
result[i] = SkMScalarToScalar(value);
}
- memcpy(dst, result, sizeof(result));
+
+ if (storage == result) {
+ memcpy(dst, storage, sizeof(storage));
+ }
}
#ifdef SK_MSCALAR_IS_DOUBLE
+
void SkMatrix44::mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
- SkMScalar result[4];
+ SkMScalar storage[4];
+ SkMScalar* result = (src == dst) ? storage : dst;
+
for (int i = 0; i < 4; i++) {
SkMScalar value = 0;
for (int j = 0; j < 4; j++) {
value += fMat[j][i] * src[j];
}
- result[i] = SkMScalarToScalar(value);
+ result[i] = value;
+ }
+
+ if (storage == result) {
+ memcpy(dst, storage, sizeof(storage));
}
- memcpy(dst, result, sizeof(result));
}
+
#endif
///////////////////////////////////////////////////////////////////////////////
@@ -445,6 +534,8 @@ void SkMatrix44::dump() const {
///////////////////////////////////////////////////////////////////////////////
+// TODO: make this support src' perspective elements
+//
static void initFromMatrix(SkMScalar dst[4][4], const SkMatrix& src) {
sk_bzero(dst, 16 * sizeof(SkMScalar));
dst[0][0] = SkScalarToMScalar(src[SkMatrix::kMScaleX]);
@@ -462,10 +553,17 @@ SkMatrix44::SkMatrix44(const SkMatrix& src) {
SkMatrix44& SkMatrix44::operator=(const SkMatrix& src) {
initFromMatrix(fMat, src);
- fIdentity = src.isIdentity();
+
+ if (src.isIdentity()) {
+ this->setTypeMask(kIdentity_Mask);
+ } else {
+ this->dirtyTypeMask();
+ }
return *this;
}
+// TODO: make this support our perspective elements
+//
SkMatrix44::operator SkMatrix() const {
SkMatrix dst;
dst.reset(); // setup our perspective correctly for identity
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
index ea6a56f436..ed8770ae6c 100644
--- a/tests/Matrix44Test.cpp
+++ b/tests/Matrix44Test.cpp
@@ -1,10 +1,10 @@
-
/*
* 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 "Test.h"
#include "SkMatrix44.h"
@@ -72,6 +72,30 @@ static bool is_identity(const SkMatrix44& m) {
return nearly_equal(m, identity);
}
+static void test_gettype(skiatest::Reporter* reporter) {
+ SkMatrix44 matrix;
+
+ 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);
+}
+
static void test_common_angles(skiatest::Reporter* reporter) {
SkMatrix44 rot;
// Test precision of rotation in common cases
@@ -125,25 +149,25 @@ static void test_concat(skiatest::Reporter* reporter) {
static void test_determinant(skiatest::Reporter* reporter) {
SkMatrix44 a;
REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
- a.set(1, 1, SkFloatToMScalar(2));
+ 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, SkFloatToMScalar(4));
- b.set(1, 0, SkFloatToMScalar(4));
+ 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, SkFloatToMScalar(8));
+ 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, SkFloatToMScalar(0));
+ e.set(0, 0, 0);
REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
}
@@ -180,9 +204,12 @@ static void test_get_set_double(skiatest::Reporter* reporter) {
static void test_set_row_col_major(skiatest::Reporter* reporter) {
SkMatrix44 a, b, c, d;
- for (int row = 0; row < 4; ++row)
- for (int col = 0; col < 4; ++col)
+ 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);
@@ -200,31 +227,26 @@ static void test_set_row_col_major(skiatest::Reporter* reporter) {
}
static void TestMatrix44(skiatest::Reporter* reporter) {
-#ifdef SK_SCALAR_IS_FLOAT
SkMatrix44 mat, inverse, iden1, iden2, rot;
mat.reset();
- mat.setTranslate(SK_Scalar1, SK_Scalar1, SK_Scalar1);
+ mat.setTranslate(1, 1, 1);
mat.invert(&inverse);
iden1.setConcat(mat, inverse);
REPORTER_ASSERT(reporter, is_identity(iden1));
- mat.setScale(SkIntToScalar(2), SkIntToScalar(2), SkIntToScalar(2));
+ 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.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
mat.invert(&inverse);
iden1.setConcat(mat, inverse);
REPORTER_ASSERT(reporter, is_identity(iden1));
- mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20));
- rot.setRotateDegreesAbout(
- SkIntToScalar(0),
- SkIntToScalar(0),
- SkIntToScalar(-1),
- SkIntToScalar(90));
+ mat.setScale(3, 3, 3);
+ rot.setRotateDegreesAbout(0, 0, -1, 90);
mat.postConcat(rot);
REPORTER_ASSERT(reporter, mat.invert(NULL));
mat.invert(&inverse);
@@ -268,10 +290,11 @@ static void TestMatrix44(skiatest::Reporter* reporter) {
test_common_angles(reporter);
}
+ test_gettype(reporter);
test_determinant(reporter);
test_transpose(reporter);
test_get_set_double(reporter);
-#endif
+ test_set_row_col_major(reporter);
}
#include "TestClassDef.h"