diff options
-rw-r--r-- | gyp/tests.gypi | 1 | ||||
-rw-r--r-- | src/core/Sk4x.h | 70 | ||||
-rw-r--r-- | src/core/Sk4x_portable.h | 101 | ||||
-rw-r--r-- | tests/Sk4xTest.cpp | 90 |
4 files changed, 262 insertions, 0 deletions
diff --git a/gyp/tests.gypi b/gyp/tests.gypi index fccbd1cf64..b21d61ec55 100644 --- a/gyp/tests.gypi +++ b/gyp/tests.gypi @@ -189,6 +189,7 @@ '../tests/ShaderImageFilterTest.cpp', '../tests/ShaderOpacityTest.cpp', '../tests/SizeTest.cpp', + '../tests/Sk4xTest.cpp', '../tests/SkBase64Test.cpp', '../tests/SkResourceCacheTest.cpp', '../tests/SmallAllocatorTest.cpp', diff --git a/src/core/Sk4x.h b/src/core/Sk4x.h new file mode 100644 index 0000000000..5b15560ede --- /dev/null +++ b/src/core/Sk4x.h @@ -0,0 +1,70 @@ +#ifndef Sk4x_DEFINED +#define Sk4x_DEFINED + +#include "SkTypes.h" + +#define SK4X_PREAMBLE 1 + #include "Sk4x_portable.h" +#undef SK4X_PREAMBLE + +template <typename T> class Sk4x; +typedef Sk4x<float> Sk4f; +typedef Sk4x<int32_t> Sk4i; + +template <typename T> class Sk4x { +public: + Sk4x(); // Uninitialized; use Sk4x(0,0,0,0) for zero. + Sk4x(T, T, T, T); + + Sk4x(const Sk4x&); + Sk4x& operator=(const Sk4x&); + + static Sk4x Load (const T[4]); + static Sk4x LoadAligned(const T[4]); + + void store (T[4]) const; + void storeAligned(T[4]) const; + + template <typename Dst> Dst reinterpret() const; + template <typename Dst> Dst cast() const; + + bool allTrue() const; + bool anyTrue() const; + + Sk4x bitNot() const; + Sk4x bitAnd(const Sk4x&) const; + Sk4x bitOr(const Sk4x&) const; + Sk4x add(const Sk4x&) const; + Sk4x subtract(const Sk4x&) const; + Sk4x multiply(const Sk4x&) const; + Sk4x divide(const Sk4x&) const; + + Sk4i equal(const Sk4x&) const; + Sk4i notEqual(const Sk4x&) const; + Sk4i lessThan(const Sk4x&) const; + Sk4i greaterThan(const Sk4x&) const; + Sk4i lessThanEqual(const Sk4x&) const; + Sk4i greaterThanEqual(const Sk4x&) const; + + static Sk4x Min(const Sk4x& a, const Sk4x& b); + static Sk4x Max(const Sk4x& a, const Sk4x& b); + + // Swizzles follow OpenCL xyzw convention. + Sk4x zwxy() const; + + // When there's a second argument, it's abcd. + static Sk4x XYAB(const Sk4x& xyzw, const Sk4x& abcd); + static Sk4x ZWCD(const Sk4x& xyzw, const Sk4x& abcd); + +private: + // It's handy to have Sk4f and Sk4i be mutual friends. + template <typename S> friend class Sk4x; + +#define SK4X_PRIVATE 1 + #include "Sk4x_portable.h" +#undef SK4X_PRIVATE +}; + +#include "Sk4x_portable.h" + +#endif//Sk4x_DEFINED diff --git a/src/core/Sk4x_portable.h b/src/core/Sk4x_portable.h new file mode 100644 index 0000000000..2f985d0d89 --- /dev/null +++ b/src/core/Sk4x_portable.h @@ -0,0 +1,101 @@ +// It is important _not_ to put header guards here. +// This file will be intentionally included three times. + +#if defined(SK4X_PREAMBLE) + +#elif defined(SK4X_PRIVATE) + typedef T Type; + typedef T Vector[4]; + + Vector fVec; + + template <int m, int a, int s, int k> + static Sk4x Shuffle(const Sk4x&, const Sk4x&); + + void set(const T vals[4]) { for (int i = 0; i < 4; i++) { fVec[i] = vals[i]; } } + +#else + +#define M(...) template <typename T> __VA_ARGS__ Sk4x<T>:: + +M() Sk4x() {} +M() Sk4x(T a, T b, T c, T d) { fVec[0] = a; fVec[1] = b; fVec[2] = c; fVec[3] = d; } + +M() Sk4x(const Sk4x<T>& other) { this->set(other.fVec); } +M(Sk4x<T>&) operator=(const Sk4x<T>& other) { this->set(other.fVec); return *this; } + +M(Sk4x<T>) Load (const T vals[4]) { Sk4x r; r.set(vals); return r; } +M(Sk4x<T>) LoadAligned(const T vals[4]) { return Load(vals); } + +M(void) store (T vals[4]) const { for (int i = 0; i < 4; i++) { vals[i] = fVec[i]; } } +M(void) storeAligned(T vals[4]) const { this->store(vals); } + +M(template <typename Dst> Dst) reinterpret() const { + Dst d; + memcpy(&d.fVec, fVec, sizeof(fVec)); + return d; +} +M(template <typename Dst> Dst) cast() const { + return Dst((typename Dst::Type)fVec[0], + (typename Dst::Type)fVec[1], + (typename Dst::Type)fVec[2], + (typename Dst::Type)fVec[3]); +} + +M(bool) allTrue() const { return fVec[0] && fVec[1] && fVec[2] && fVec[3]; } +M(bool) anyTrue() const { return fVec[0] || fVec[1] || fVec[2] || fVec[3]; } + +M(Sk4x<T>) bitNot() const { return Sk4x(~fVec[0], ~fVec[1], ~fVec[2], ~fVec[3]); } + +#define BINOP(op) fVec[0] op other.fVec[0], \ + fVec[1] op other.fVec[1], \ + fVec[2] op other.fVec[2], \ + fVec[3] op other.fVec[3] +M(Sk4x<T>) bitAnd(const Sk4x& other) const { return Sk4x(BINOP(&)); } +M(Sk4x<T>) bitOr(const Sk4x& other) const { return Sk4x(BINOP(|)); } +M(Sk4x<T>) add(const Sk4x<T>& other) const { return Sk4x(BINOP(+)); } +M(Sk4x<T>) subtract(const Sk4x<T>& other) const { return Sk4x(BINOP(-)); } +M(Sk4x<T>) multiply(const Sk4x<T>& other) const { return Sk4x(BINOP(*)); } +M(Sk4x<T>) divide(const Sk4x<T>& other) const { return Sk4x(BINOP(/)); } +#undef BINOP + +#define BOOL_BINOP(op) fVec[0] op other.fVec[0] ? -1 : 0, \ + fVec[1] op other.fVec[1] ? -1 : 0, \ + fVec[2] op other.fVec[2] ? -1 : 0, \ + fVec[3] op other.fVec[3] ? -1 : 0 +M(Sk4i) equal(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(==)); } +M(Sk4i) notEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(!=)); } +M(Sk4i) lessThan(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP( <)); } +M(Sk4i) greaterThan(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP( >)); } +M(Sk4i) lessThanEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(<=)); } +M(Sk4i) greaterThanEqual(const Sk4x<T>& other) const { return Sk4i(BOOL_BINOP(>=)); } +#undef BOOL_BINOP + +M(Sk4x<T>) Min(const Sk4x<T>& a, const Sk4x<T>& b) { + return Sk4x(SkTMin(a.fVec[0], b.fVec[0]), + SkTMin(a.fVec[1], b.fVec[1]), + SkTMin(a.fVec[2], b.fVec[2]), + SkTMin(a.fVec[3], b.fVec[3])); +} + +M(Sk4x<T>) Max(const Sk4x<T>& a, const Sk4x<T>& b) { + return Sk4x(SkTMax(a.fVec[0], b.fVec[0]), + SkTMax(a.fVec[1], b.fVec[1]), + SkTMax(a.fVec[2], b.fVec[2]), + SkTMax(a.fVec[3], b.fVec[3])); +} + +M(template <int m, int a, int s, int k> Sk4x<T>) Shuffle(const Sk4x<T>& x, const Sk4x<T>& y) { + return Sk4x(m < 4 ? x.fVec[m] : y.fVec[m-4], + a < 4 ? x.fVec[a] : y.fVec[a-4], + s < 4 ? x.fVec[s] : y.fVec[s-4], + k < 4 ? x.fVec[k] : y.fVec[k-4]); +} + +M(Sk4x<T>) zwxy() const { return Shuffle<2,3,0,1>(*this, *this); } +M(Sk4x<T>) XYAB(const Sk4x& xyzw, const Sk4x& abcd) { return Shuffle<0,1,4,5>( xyzw, abcd); } +M(Sk4x<T>) ZWCD(const Sk4x& xyzw, const Sk4x& abcd) { return Shuffle<2,3,6,7>( xyzw, abcd); } + +#undef M + +#endif diff --git a/tests/Sk4xTest.cpp b/tests/Sk4xTest.cpp new file mode 100644 index 0000000000..30135a3f39 --- /dev/null +++ b/tests/Sk4xTest.cpp @@ -0,0 +1,90 @@ +#include "Test.h" +#include "Sk4x.h" + +#define ASSERT_EQ(a, b) REPORTER_ASSERT(r, a.equal(b).allTrue()) +#define ASSERT_NE(a, b) REPORTER_ASSERT(r, a.notEqual(b).allTrue()) + +DEF_TEST(Sk4x_Construction, r) { + Sk4f uninitialized; + Sk4f zero(0,0,0,0); + Sk4f foo(1,2,3,4), + bar(foo), + baz = bar; + ASSERT_EQ(foo, bar); + ASSERT_EQ(bar, baz); + ASSERT_EQ(baz, foo); +} + +DEF_TEST(Sk4x_LoadStore, r) { + // fs will be 16-byte aligned, fs+1 not. + float fs[] = { 5,6,7,8,9 }; + + Sk4f foo = Sk4f::Load(fs); + Sk4f bar = Sk4f::LoadAligned(fs); + ASSERT_EQ(foo, bar); + + foo = Sk4f::Load(fs+1); + ASSERT_NE(foo, bar); + + foo.storeAligned(fs); + bar.store(fs+1); + REPORTER_ASSERT(r, fs[0] == 6 && + fs[1] == 5 && + fs[2] == 6 && + fs[3] == 7 && + fs[4] == 8); +} + +DEF_TEST(Sk4x_Conversions, r) { + // Assuming IEEE floats. + Sk4f zerof(0,0,0,0); + Sk4i zeroi(0,0,0,0); + ASSERT_EQ(zeroi, zerof.cast<Sk4i>()); + ASSERT_EQ(zeroi, zerof.reinterpret<Sk4i>()); + + Sk4f twof(2,2,2,2); + Sk4i twoi(2,2,2,2); + ASSERT_EQ(twoi, twof.cast<Sk4i>()); + ASSERT_NE(twoi, twof.reinterpret<Sk4i>()); +} + +DEF_TEST(Sk4x_Bits, r) { + ASSERT_EQ(Sk4i(0,0,0,0).bitNot(), Sk4i(-1,-1,-1,-1)); + + Sk4i a(2,3,4,5), + b(1,3,5,7); + ASSERT_EQ(Sk4i(0,3,4,5), a.bitAnd(b)); + ASSERT_EQ(Sk4i(3,3,5,7), a.bitOr(b)); +} + +DEF_TEST(Sk4x_Arith, r) { + ASSERT_EQ(Sk4f(4,6,8,10), Sk4f(1,2,3,4).add(Sk4f(3,4,5,6))); + ASSERT_EQ(Sk4f(-2,-2,-2,-2), Sk4f(1,2,3,4).subtract(Sk4f(3,4,5,6))); + ASSERT_EQ(Sk4f(3,8,15,24), Sk4f(1,2,3,4).multiply(Sk4f(3,4,5,6))); + + float third = 1.0f/3.0f; + ASSERT_EQ(Sk4f(1*third, 0.5f, 0.6f, 2*third), Sk4f(1,2,3,4).divide(Sk4f(3,4,5,6))); +} + +DEF_TEST(Sk4x_Comparison, r) { + ASSERT_EQ(Sk4f(1,2,3,4), Sk4f(1,2,3,4)); + ASSERT_NE(Sk4f(4,3,2,1), Sk4f(1,2,3,4)); + + ASSERT_EQ(Sk4i(-1,-1,0,-1), Sk4f(1,2,5,4).equal(Sk4f(1,2,3,4))); + + ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4f(1,2,3,4).lessThan(Sk4f(2,3,4,5))); + ASSERT_EQ(Sk4i(-1,-1,-1,-1), Sk4f(1,2,3,4).lessThanEqual(Sk4f(2,3,4,5))); + ASSERT_EQ(Sk4i(0,0,0,0), Sk4f(1,2,3,4).greaterThan(Sk4f(2,3,4,5))); + ASSERT_EQ(Sk4i(0,0,0,0), Sk4f(1,2,3,4).greaterThanEqual(Sk4f(2,3,4,5))); +} + +DEF_TEST(Sk4x_MinMax, r) { + ASSERT_EQ(Sk4f(1,2,2,1), Sk4f::Min(Sk4f(1,2,3,4), Sk4f(4,3,2,1))); + ASSERT_EQ(Sk4f(4,3,3,4), Sk4f::Max(Sk4f(1,2,3,4), Sk4f(4,3,2,1))); +} + +DEF_TEST(Sk4x_Swizzle, r) { + ASSERT_EQ(Sk4f(3,4,1,2), Sk4f(1,2,3,4).zwxy()); + ASSERT_EQ(Sk4f(1,2,5,6), Sk4f::XYAB(Sk4f(1,2,3,4), Sk4f(5,6,7,8))); + ASSERT_EQ(Sk4f(3,4,7,8), Sk4f::ZWCD(Sk4f(1,2,3,4), Sk4f(5,6,7,8))); +} |