| 1 | /* | 
|---|
| 2 | * Copyright 2015 Google Inc. | 
|---|
| 3 | * | 
|---|
| 4 | * Use of this source code is governed by a BSD-style license that can be | 
|---|
| 5 | * found in the LICENSE file. | 
|---|
| 6 | */ | 
|---|
| 7 |  | 
|---|
| 8 | #ifndef SkNx_DEFINED | 
|---|
| 9 | #define SkNx_DEFINED | 
|---|
| 10 |  | 
|---|
| 11 | #include "include/core/SkScalar.h" | 
|---|
| 12 | #include "include/core/SkTypes.h" | 
|---|
| 13 | #include "include/private/SkSafe_math.h" | 
|---|
| 14 |  | 
|---|
| 15 | #include <algorithm> | 
|---|
| 16 | #include <limits> | 
|---|
| 17 | #include <type_traits> | 
|---|
| 18 |  | 
|---|
| 19 | // Every single SkNx method wants to be fully inlined.  (We know better than MSVC). | 
|---|
| 20 | #define AI SK_ALWAYS_INLINE | 
|---|
| 21 |  | 
|---|
| 22 | namespace {  // NOLINT(google-build-namespaces) | 
|---|
| 23 |  | 
|---|
| 24 | // The default SkNx<N,T> just proxies down to a pair of SkNx<N/2, T>. | 
|---|
| 25 | template <int N, typename T> | 
|---|
| 26 | struct SkNx { | 
|---|
| 27 | typedef SkNx<N/2, T> Half; | 
|---|
| 28 |  | 
|---|
| 29 | Half fLo, fHi; | 
|---|
| 30 |  | 
|---|
| 31 | AI SkNx() = default; | 
|---|
| 32 | AI SkNx(const Half& lo, const Half& hi) : fLo(lo), fHi(hi) {} | 
|---|
| 33 |  | 
|---|
| 34 | AI SkNx(T v) : fLo(v), fHi(v) {} | 
|---|
| 35 |  | 
|---|
| 36 | AI SkNx(T a, T b)           : fLo(a)  , fHi(b)   { static_assert(N==2, ""); } | 
|---|
| 37 | AI SkNx(T a, T b, T c, T d) : fLo(a,b), fHi(c,d) { static_assert(N==4, ""); } | 
|---|
| 38 | AI SkNx(T a, T b, T c, T d,  T e, T f, T g, T h) : fLo(a,b,c,d), fHi(e,f,g,h) { | 
|---|
| 39 | static_assert(N==8, ""); | 
|---|
| 40 | } | 
|---|
| 41 | AI SkNx(T a, T b, T c, T d,  T e, T f, T g, T h, | 
|---|
| 42 | T i, T j, T k, T l,  T m, T n, T o, T p) | 
|---|
| 43 | : fLo(a,b,c,d, e,f,g,h), fHi(i,j,k,l, m,n,o,p) { | 
|---|
| 44 | static_assert(N==16, ""); | 
|---|
| 45 | } | 
|---|
| 46 |  | 
|---|
| 47 | AI T operator[](int k) const { | 
|---|
| 48 | SkASSERT(0 <= k && k < N); | 
|---|
| 49 | return k < N/2 ? fLo[k] : fHi[k-N/2]; | 
|---|
| 50 | } | 
|---|
| 51 |  | 
|---|
| 52 | AI static SkNx Load(const void* vptr) { | 
|---|
| 53 | auto ptr = (const char*)vptr; | 
|---|
| 54 | return { Half::Load(ptr), Half::Load(ptr + N/2*sizeof(T)) }; | 
|---|
| 55 | } | 
|---|
| 56 | AI void store(void* vptr) const { | 
|---|
| 57 | auto ptr = (char*)vptr; | 
|---|
| 58 | fLo.store(ptr); | 
|---|
| 59 | fHi.store(ptr + N/2*sizeof(T)); | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | AI static void Load4(const void* vptr, SkNx* a, SkNx* b, SkNx* c, SkNx* d) { | 
|---|
| 63 | auto ptr = (const char*)vptr; | 
|---|
| 64 | Half al, bl, cl, dl, | 
|---|
| 65 | ah, bh, ch, dh; | 
|---|
| 66 | Half::Load4(ptr                  , &al, &bl, &cl, &dl); | 
|---|
| 67 | Half::Load4(ptr + 4*N/2*sizeof(T), &ah, &bh, &ch, &dh); | 
|---|
| 68 | *a = SkNx{al, ah}; | 
|---|
| 69 | *b = SkNx{bl, bh}; | 
|---|
| 70 | *c = SkNx{cl, ch}; | 
|---|
| 71 | *d = SkNx{dl, dh}; | 
|---|
| 72 | } | 
|---|
| 73 | AI static void Load3(const void* vptr, SkNx* a, SkNx* b, SkNx* c) { | 
|---|
| 74 | auto ptr = (const char*)vptr; | 
|---|
| 75 | Half al, bl, cl, | 
|---|
| 76 | ah, bh, ch; | 
|---|
| 77 | Half::Load3(ptr                  , &al, &bl, &cl); | 
|---|
| 78 | Half::Load3(ptr + 3*N/2*sizeof(T), &ah, &bh, &ch); | 
|---|
| 79 | *a = SkNx{al, ah}; | 
|---|
| 80 | *b = SkNx{bl, bh}; | 
|---|
| 81 | *c = SkNx{cl, ch}; | 
|---|
| 82 | } | 
|---|
| 83 | AI static void Load2(const void* vptr, SkNx* a, SkNx* b) { | 
|---|
| 84 | auto ptr = (const char*)vptr; | 
|---|
| 85 | Half al, bl, | 
|---|
| 86 | ah, bh; | 
|---|
| 87 | Half::Load2(ptr                  , &al, &bl); | 
|---|
| 88 | Half::Load2(ptr + 2*N/2*sizeof(T), &ah, &bh); | 
|---|
| 89 | *a = SkNx{al, ah}; | 
|---|
| 90 | *b = SkNx{bl, bh}; | 
|---|
| 91 | } | 
|---|
| 92 | AI static void Store4(void* vptr, const SkNx& a, const SkNx& b, const SkNx& c, const SkNx& d) { | 
|---|
| 93 | auto ptr = (char*)vptr; | 
|---|
| 94 | Half::Store4(ptr,                   a.fLo, b.fLo, c.fLo, d.fLo); | 
|---|
| 95 | Half::Store4(ptr + 4*N/2*sizeof(T), a.fHi, b.fHi, c.fHi, d.fHi); | 
|---|
| 96 | } | 
|---|
| 97 | AI static void Store3(void* vptr, const SkNx& a, const SkNx& b, const SkNx& c) { | 
|---|
| 98 | auto ptr = (char*)vptr; | 
|---|
| 99 | Half::Store3(ptr,                   a.fLo, b.fLo, c.fLo); | 
|---|
| 100 | Half::Store3(ptr + 3*N/2*sizeof(T), a.fHi, b.fHi, c.fHi); | 
|---|
| 101 | } | 
|---|
| 102 | AI static void Store2(void* vptr, const SkNx& a, const SkNx& b) { | 
|---|
| 103 | auto ptr = (char*)vptr; | 
|---|
| 104 | Half::Store2(ptr,                   a.fLo, b.fLo); | 
|---|
| 105 | Half::Store2(ptr + 2*N/2*sizeof(T), a.fHi, b.fHi); | 
|---|
| 106 | } | 
|---|
| 107 |  | 
|---|
| 108 | AI T min() const { return std::min(fLo.min(), fHi.min()); } | 
|---|
| 109 | AI T max() const { return std::max(fLo.max(), fHi.max()); } | 
|---|
| 110 | AI bool anyTrue() const { return fLo.anyTrue() || fHi.anyTrue(); } | 
|---|
| 111 | AI bool allTrue() const { return fLo.allTrue() && fHi.allTrue(); } | 
|---|
| 112 |  | 
|---|
| 113 | AI SkNx    abs() const { return { fLo.   abs(), fHi.   abs() }; } | 
|---|
| 114 | AI SkNx   sqrt() const { return { fLo.  sqrt(), fHi.  sqrt() }; } | 
|---|
| 115 | AI SkNx  rsqrt() const { return { fLo. rsqrt(), fHi. rsqrt() }; } | 
|---|
| 116 | AI SkNx  floor() const { return { fLo. floor(), fHi. floor() }; } | 
|---|
| 117 | AI SkNx invert() const { return { fLo.invert(), fHi.invert() }; } | 
|---|
| 118 |  | 
|---|
| 119 | AI SkNx operator!() const { return { !fLo, !fHi }; } | 
|---|
| 120 | AI SkNx operator-() const { return { -fLo, -fHi }; } | 
|---|
| 121 | AI SkNx operator~() const { return { ~fLo, ~fHi }; } | 
|---|
| 122 |  | 
|---|
| 123 | AI SkNx operator<<(int bits) const { return { fLo << bits, fHi << bits }; } | 
|---|
| 124 | AI SkNx operator>>(int bits) const { return { fLo >> bits, fHi >> bits }; } | 
|---|
| 125 |  | 
|---|
| 126 | AI SkNx operator+(const SkNx& y) const { return { fLo + y.fLo, fHi + y.fHi }; } | 
|---|
| 127 | AI SkNx operator-(const SkNx& y) const { return { fLo - y.fLo, fHi - y.fHi }; } | 
|---|
| 128 | AI SkNx operator*(const SkNx& y) const { return { fLo * y.fLo, fHi * y.fHi }; } | 
|---|
| 129 | AI SkNx operator/(const SkNx& y) const { return { fLo / y.fLo, fHi / y.fHi }; } | 
|---|
| 130 |  | 
|---|
| 131 | AI SkNx operator&(const SkNx& y) const { return { fLo & y.fLo, fHi & y.fHi }; } | 
|---|
| 132 | AI SkNx operator|(const SkNx& y) const { return { fLo | y.fLo, fHi | y.fHi }; } | 
|---|
| 133 | AI SkNx operator^(const SkNx& y) const { return { fLo ^ y.fLo, fHi ^ y.fHi }; } | 
|---|
| 134 |  | 
|---|
| 135 | AI SkNx operator==(const SkNx& y) const { return { fLo == y.fLo, fHi == y.fHi }; } | 
|---|
| 136 | AI SkNx operator!=(const SkNx& y) const { return { fLo != y.fLo, fHi != y.fHi }; } | 
|---|
| 137 | AI SkNx operator<=(const SkNx& y) const { return { fLo <= y.fLo, fHi <= y.fHi }; } | 
|---|
| 138 | AI SkNx operator>=(const SkNx& y) const { return { fLo >= y.fLo, fHi >= y.fHi }; } | 
|---|
| 139 | AI SkNx operator< (const SkNx& y) const { return { fLo <  y.fLo, fHi <  y.fHi }; } | 
|---|
| 140 | AI SkNx operator> (const SkNx& y) const { return { fLo >  y.fLo, fHi >  y.fHi }; } | 
|---|
| 141 |  | 
|---|
| 142 | AI SkNx saturatedAdd(const SkNx& y) const { | 
|---|
| 143 | return { fLo.saturatedAdd(y.fLo), fHi.saturatedAdd(y.fHi) }; | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | AI SkNx mulHi(const SkNx& m) const { | 
|---|
| 147 | return { fLo.mulHi(m.fLo), fHi.mulHi(m.fHi) }; | 
|---|
| 148 | } | 
|---|
| 149 | AI SkNx thenElse(const SkNx& t, const SkNx& e) const { | 
|---|
| 150 | return { fLo.thenElse(t.fLo, e.fLo), fHi.thenElse(t.fHi, e.fHi) }; | 
|---|
| 151 | } | 
|---|
| 152 | AI static SkNx Min(const SkNx& x, const SkNx& y) { | 
|---|
| 153 | return { Half::Min(x.fLo, y.fLo), Half::Min(x.fHi, y.fHi) }; | 
|---|
| 154 | } | 
|---|
| 155 | AI static SkNx Max(const SkNx& x, const SkNx& y) { | 
|---|
| 156 | return { Half::Max(x.fLo, y.fLo), Half::Max(x.fHi, y.fHi) }; | 
|---|
| 157 | } | 
|---|
| 158 | }; | 
|---|
| 159 |  | 
|---|
| 160 | // The N -> N/2 recursion bottoms out at N == 1, a scalar value. | 
|---|
| 161 | template <typename T> | 
|---|
| 162 | struct SkNx<1,T> { | 
|---|
| 163 | T fVal; | 
|---|
| 164 |  | 
|---|
| 165 | AI SkNx() = default; | 
|---|
| 166 | AI SkNx(T v) : fVal(v) {} | 
|---|
| 167 |  | 
|---|
| 168 | // Android complains against unused parameters, so we guard it | 
|---|
| 169 | AI T operator[](int SkDEBUGCODE(k)) const { | 
|---|
| 170 | SkASSERT(k == 0); | 
|---|
| 171 | return fVal; | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 | AI static SkNx Load(const void* ptr) { | 
|---|
| 175 | SkNx v; | 
|---|
| 176 | memcpy(&v, ptr, sizeof(T)); | 
|---|
| 177 | return v; | 
|---|
| 178 | } | 
|---|
| 179 | AI void store(void* ptr) const { memcpy(ptr, &fVal, sizeof(T)); } | 
|---|
| 180 |  | 
|---|
| 181 | AI static void Load4(const void* vptr, SkNx* a, SkNx* b, SkNx* c, SkNx* d) { | 
|---|
| 182 | auto ptr = (const char*)vptr; | 
|---|
| 183 | *a = Load(ptr + 0*sizeof(T)); | 
|---|
| 184 | *b = Load(ptr + 1*sizeof(T)); | 
|---|
| 185 | *c = Load(ptr + 2*sizeof(T)); | 
|---|
| 186 | *d = Load(ptr + 3*sizeof(T)); | 
|---|
| 187 | } | 
|---|
| 188 | AI static void Load3(const void* vptr, SkNx* a, SkNx* b, SkNx* c) { | 
|---|
| 189 | auto ptr = (const char*)vptr; | 
|---|
| 190 | *a = Load(ptr + 0*sizeof(T)); | 
|---|
| 191 | *b = Load(ptr + 1*sizeof(T)); | 
|---|
| 192 | *c = Load(ptr + 2*sizeof(T)); | 
|---|
| 193 | } | 
|---|
| 194 | AI static void Load2(const void* vptr, SkNx* a, SkNx* b) { | 
|---|
| 195 | auto ptr = (const char*)vptr; | 
|---|
| 196 | *a = Load(ptr + 0*sizeof(T)); | 
|---|
| 197 | *b = Load(ptr + 1*sizeof(T)); | 
|---|
| 198 | } | 
|---|
| 199 | AI static void Store4(void* vptr, const SkNx& a, const SkNx& b, const SkNx& c, const SkNx& d) { | 
|---|
| 200 | auto ptr = (char*)vptr; | 
|---|
| 201 | a.store(ptr + 0*sizeof(T)); | 
|---|
| 202 | b.store(ptr + 1*sizeof(T)); | 
|---|
| 203 | c.store(ptr + 2*sizeof(T)); | 
|---|
| 204 | d.store(ptr + 3*sizeof(T)); | 
|---|
| 205 | } | 
|---|
| 206 | AI static void Store3(void* vptr, const SkNx& a, const SkNx& b, const SkNx& c) { | 
|---|
| 207 | auto ptr = (char*)vptr; | 
|---|
| 208 | a.store(ptr + 0*sizeof(T)); | 
|---|
| 209 | b.store(ptr + 1*sizeof(T)); | 
|---|
| 210 | c.store(ptr + 2*sizeof(T)); | 
|---|
| 211 | } | 
|---|
| 212 | AI static void Store2(void* vptr, const SkNx& a, const SkNx& b) { | 
|---|
| 213 | auto ptr = (char*)vptr; | 
|---|
| 214 | a.store(ptr + 0*sizeof(T)); | 
|---|
| 215 | b.store(ptr + 1*sizeof(T)); | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | AI T min() const { return fVal; } | 
|---|
| 219 | AI T max() const { return fVal; } | 
|---|
| 220 | AI bool anyTrue() const { return fVal != 0; } | 
|---|
| 221 | AI bool allTrue() const { return fVal != 0; } | 
|---|
| 222 |  | 
|---|
| 223 | AI SkNx    abs() const { return Abs(fVal); } | 
|---|
| 224 | AI SkNx   sqrt() const { return Sqrt(fVal); } | 
|---|
| 225 | AI SkNx  rsqrt() const { return T(1) / this->sqrt(); } | 
|---|
| 226 | AI SkNx  floor() const { return Floor(fVal); } | 
|---|
| 227 | AI SkNx invert() const { return T(1) / *this; } | 
|---|
| 228 |  | 
|---|
| 229 | AI SkNx operator!() const { return !fVal; } | 
|---|
| 230 | AI SkNx operator-() const { return -fVal; } | 
|---|
| 231 | AI SkNx operator~() const { return FromBits(~ToBits(fVal)); } | 
|---|
| 232 |  | 
|---|
| 233 | AI SkNx operator<<(int bits) const { return fVal << bits; } | 
|---|
| 234 | AI SkNx operator>>(int bits) const { return fVal >> bits; } | 
|---|
| 235 |  | 
|---|
| 236 | AI SkNx operator+(const SkNx& y) const { return fVal + y.fVal; } | 
|---|
| 237 | AI SkNx operator-(const SkNx& y) const { return fVal - y.fVal; } | 
|---|
| 238 | AI SkNx operator*(const SkNx& y) const { return fVal * y.fVal; } | 
|---|
| 239 | AI SkNx operator/(const SkNx& y) const { return fVal / y.fVal; } | 
|---|
| 240 |  | 
|---|
| 241 | AI SkNx operator&(const SkNx& y) const { return FromBits(ToBits(fVal) & ToBits(y.fVal)); } | 
|---|
| 242 | AI SkNx operator|(const SkNx& y) const { return FromBits(ToBits(fVal) | ToBits(y.fVal)); } | 
|---|
| 243 | AI SkNx operator^(const SkNx& y) const { return FromBits(ToBits(fVal) ^ ToBits(y.fVal)); } | 
|---|
| 244 |  | 
|---|
| 245 | AI SkNx operator==(const SkNx& y) const { return FromBits(fVal == y.fVal ? ~0 : 0); } | 
|---|
| 246 | AI SkNx operator!=(const SkNx& y) const { return FromBits(fVal != y.fVal ? ~0 : 0); } | 
|---|
| 247 | AI SkNx operator<=(const SkNx& y) const { return FromBits(fVal <= y.fVal ? ~0 : 0); } | 
|---|
| 248 | AI SkNx operator>=(const SkNx& y) const { return FromBits(fVal >= y.fVal ? ~0 : 0); } | 
|---|
| 249 | AI SkNx operator< (const SkNx& y) const { return FromBits(fVal <  y.fVal ? ~0 : 0); } | 
|---|
| 250 | AI SkNx operator> (const SkNx& y) const { return FromBits(fVal >  y.fVal ? ~0 : 0); } | 
|---|
| 251 |  | 
|---|
| 252 | AI static SkNx Min(const SkNx& x, const SkNx& y) { return x.fVal < y.fVal ? x : y; } | 
|---|
| 253 | AI static SkNx Max(const SkNx& x, const SkNx& y) { return x.fVal > y.fVal ? x : y; } | 
|---|
| 254 |  | 
|---|
| 255 | AI SkNx saturatedAdd(const SkNx& y) const { | 
|---|
| 256 | static_assert(std::is_unsigned<T>::value, ""); | 
|---|
| 257 | T sum = fVal + y.fVal; | 
|---|
| 258 | return sum < fVal ? std::numeric_limits<T>::max() : sum; | 
|---|
| 259 | } | 
|---|
| 260 |  | 
|---|
| 261 | AI SkNx mulHi(const SkNx& m) const { | 
|---|
| 262 | static_assert(std::is_unsigned<T>::value, ""); | 
|---|
| 263 | static_assert(sizeof(T) <= 4, ""); | 
|---|
| 264 | return static_cast<T>((static_cast<uint64_t>(fVal) * m.fVal) >> (sizeof(T)*8)); | 
|---|
| 265 | } | 
|---|
| 266 |  | 
|---|
| 267 | AI SkNx thenElse(const SkNx& t, const SkNx& e) const { return fVal != 0 ? t : e; } | 
|---|
| 268 |  | 
|---|
| 269 | private: | 
|---|
| 270 | // Helper functions to choose the right float/double methods.  (In <cmath> madness lies...) | 
|---|
| 271 | AI static int     Abs(int val) { return  val < 0 ? -val : val; } | 
|---|
| 272 |  | 
|---|
| 273 | AI static float   Abs(float val) { return  ::fabsf(val); } | 
|---|
| 274 | AI static float  Sqrt(float val) { return  ::sqrtf(val); } | 
|---|
| 275 | AI static float Floor(float val) { return ::floorf(val); } | 
|---|
| 276 |  | 
|---|
| 277 | AI static double   Abs(double val) { return  ::fabs(val); } | 
|---|
| 278 | AI static double  Sqrt(double val) { return  ::sqrt(val); } | 
|---|
| 279 | AI static double Floor(double val) { return ::floor(val); } | 
|---|
| 280 |  | 
|---|
| 281 | // Helper functions for working with floats/doubles as bit patterns. | 
|---|
| 282 | template <typename U> | 
|---|
| 283 | AI static U ToBits(U v) { return v; } | 
|---|
| 284 | AI static int32_t ToBits(float  v) { int32_t bits; memcpy(&bits, &v, sizeof(v)); return bits; } | 
|---|
| 285 | AI static int64_t ToBits(double v) { int64_t bits; memcpy(&bits, &v, sizeof(v)); return bits; } | 
|---|
| 286 |  | 
|---|
| 287 | template <typename Bits> | 
|---|
| 288 | AI static T FromBits(Bits bits) { | 
|---|
| 289 | static_assert(std::is_pod<T   >::value && | 
|---|
| 290 | std::is_pod<Bits>::value && | 
|---|
| 291 | sizeof(T) <= sizeof(Bits), ""); | 
|---|
| 292 | T val; | 
|---|
| 293 | memcpy(&val, &bits, sizeof(T)); | 
|---|
| 294 | return val; | 
|---|
| 295 | } | 
|---|
| 296 | }; | 
|---|
| 297 |  | 
|---|
| 298 | // Allow scalars on the left or right of binary operators, and things like +=, &=, etc. | 
|---|
| 299 | #define V template <int N, typename T> AI static SkNx<N,T> | 
|---|
| 300 | V operator+ (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) +  y; } | 
|---|
| 301 | V operator- (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) -  y; } | 
|---|
| 302 | V operator* (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) *  y; } | 
|---|
| 303 | V operator/ (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) /  y; } | 
|---|
| 304 | V operator& (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) &  y; } | 
|---|
| 305 | V operator| (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) |  y; } | 
|---|
| 306 | V operator^ (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) ^  y; } | 
|---|
| 307 | V operator==(T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) == y; } | 
|---|
| 308 | V operator!=(T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) != y; } | 
|---|
| 309 | V operator<=(T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) <= y; } | 
|---|
| 310 | V operator>=(T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) >= y; } | 
|---|
| 311 | V operator< (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) <  y; } | 
|---|
| 312 | V operator> (T x, const SkNx<N,T>& y) { return SkNx<N,T>(x) >  y; } | 
|---|
| 313 |  | 
|---|
| 314 | V operator+ (const SkNx<N,T>& x, T y) { return x +  SkNx<N,T>(y); } | 
|---|
| 315 | V operator- (const SkNx<N,T>& x, T y) { return x -  SkNx<N,T>(y); } | 
|---|
| 316 | V operator* (const SkNx<N,T>& x, T y) { return x *  SkNx<N,T>(y); } | 
|---|
| 317 | V operator/ (const SkNx<N,T>& x, T y) { return x /  SkNx<N,T>(y); } | 
|---|
| 318 | V operator& (const SkNx<N,T>& x, T y) { return x &  SkNx<N,T>(y); } | 
|---|
| 319 | V operator| (const SkNx<N,T>& x, T y) { return x |  SkNx<N,T>(y); } | 
|---|
| 320 | V operator^ (const SkNx<N,T>& x, T y) { return x ^  SkNx<N,T>(y); } | 
|---|
| 321 | V operator==(const SkNx<N,T>& x, T y) { return x == SkNx<N,T>(y); } | 
|---|
| 322 | V operator!=(const SkNx<N,T>& x, T y) { return x != SkNx<N,T>(y); } | 
|---|
| 323 | V operator<=(const SkNx<N,T>& x, T y) { return x <= SkNx<N,T>(y); } | 
|---|
| 324 | V operator>=(const SkNx<N,T>& x, T y) { return x >= SkNx<N,T>(y); } | 
|---|
| 325 | V operator< (const SkNx<N,T>& x, T y) { return x <  SkNx<N,T>(y); } | 
|---|
| 326 | V operator> (const SkNx<N,T>& x, T y) { return x >  SkNx<N,T>(y); } | 
|---|
| 327 |  | 
|---|
| 328 | V& operator<<=(SkNx<N,T>& x, int bits) { return (x = x << bits); } | 
|---|
| 329 | V& operator>>=(SkNx<N,T>& x, int bits) { return (x = x >> bits); } | 
|---|
| 330 |  | 
|---|
| 331 | V& operator +=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x + y); } | 
|---|
| 332 | V& operator -=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x - y); } | 
|---|
| 333 | V& operator *=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x * y); } | 
|---|
| 334 | V& operator /=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x / y); } | 
|---|
| 335 | V& operator &=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x & y); } | 
|---|
| 336 | V& operator |=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x | y); } | 
|---|
| 337 | V& operator ^=(SkNx<N,T>& x, const SkNx<N,T>& y) { return (x = x ^ y); } | 
|---|
| 338 |  | 
|---|
| 339 | V& operator +=(SkNx<N,T>& x, T y) { return (x = x + SkNx<N,T>(y)); } | 
|---|
| 340 | V& operator -=(SkNx<N,T>& x, T y) { return (x = x - SkNx<N,T>(y)); } | 
|---|
| 341 | V& operator *=(SkNx<N,T>& x, T y) { return (x = x * SkNx<N,T>(y)); } | 
|---|
| 342 | V& operator /=(SkNx<N,T>& x, T y) { return (x = x / SkNx<N,T>(y)); } | 
|---|
| 343 | V& operator &=(SkNx<N,T>& x, T y) { return (x = x & SkNx<N,T>(y)); } | 
|---|
| 344 | V& operator |=(SkNx<N,T>& x, T y) { return (x = x | SkNx<N,T>(y)); } | 
|---|
| 345 | V& operator ^=(SkNx<N,T>& x, T y) { return (x = x ^ SkNx<N,T>(y)); } | 
|---|
| 346 | #undef V | 
|---|
| 347 |  | 
|---|
| 348 | // SkNx<N,T> ~~> SkNx<N/2,T> + SkNx<N/2,T> | 
|---|
| 349 | template <int N, typename T> | 
|---|
| 350 | AI static void SkNx_split(const SkNx<N,T>& v, SkNx<N/2,T>* lo, SkNx<N/2,T>* hi) { | 
|---|
| 351 | *lo = v.fLo; | 
|---|
| 352 | *hi = v.fHi; | 
|---|
| 353 | } | 
|---|
| 354 |  | 
|---|
| 355 | // SkNx<N/2,T> + SkNx<N/2,T> ~~> SkNx<N,T> | 
|---|
| 356 | template <int N, typename T> | 
|---|
| 357 | AI static SkNx<N*2,T> SkNx_join(const SkNx<N,T>& lo, const SkNx<N,T>& hi) { | 
|---|
| 358 | return { lo, hi }; | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | // A very generic shuffle.  Can reorder, duplicate, contract, expand... | 
|---|
| 362 | //    Sk4f v = { R,G,B,A }; | 
|---|
| 363 | //    SkNx_shuffle<2,1,0,3>(v)         ~~> {B,G,R,A} | 
|---|
| 364 | //    SkNx_shuffle<2,1>(v)             ~~> {B,G} | 
|---|
| 365 | //    SkNx_shuffle<2,1,2,1,2,1,2,1>(v) ~~> {B,G,B,G,B,G,B,G} | 
|---|
| 366 | //    SkNx_shuffle<3,3,3,3>(v)         ~~> {A,A,A,A} | 
|---|
| 367 | template <int... Ix, int N, typename T> | 
|---|
| 368 | AI static SkNx<sizeof...(Ix),T> SkNx_shuffle(const SkNx<N,T>& v) { | 
|---|
| 369 | return { v[Ix]... }; | 
|---|
| 370 | } | 
|---|
| 371 |  | 
|---|
| 372 | // Cast from SkNx<N, Src> to SkNx<N, Dst>, as if you called static_cast<Dst>(Src). | 
|---|
| 373 | template <typename Dst, typename Src, int N> | 
|---|
| 374 | AI static SkNx<N,Dst> SkNx_cast(const SkNx<N,Src>& v) { | 
|---|
| 375 | return { SkNx_cast<Dst>(v.fLo), SkNx_cast<Dst>(v.fHi) }; | 
|---|
| 376 | } | 
|---|
| 377 | template <typename Dst, typename Src> | 
|---|
| 378 | AI static SkNx<1,Dst> SkNx_cast(const SkNx<1,Src>& v) { | 
|---|
| 379 | return static_cast<Dst>(v.fVal); | 
|---|
| 380 | } | 
|---|
| 381 |  | 
|---|
| 382 | template <int N, typename T> | 
|---|
| 383 | AI static SkNx<N,T> SkNx_fma(const SkNx<N,T>& f, const SkNx<N,T>& m, const SkNx<N,T>& a) { | 
|---|
| 384 | return f*m+a; | 
|---|
| 385 | } | 
|---|
| 386 |  | 
|---|
| 387 | }  // namespace | 
|---|
| 388 |  | 
|---|
| 389 | typedef SkNx<2,     float> Sk2f; | 
|---|
| 390 | typedef SkNx<4,     float> Sk4f; | 
|---|
| 391 | typedef SkNx<8,     float> Sk8f; | 
|---|
| 392 | typedef SkNx<16,    float> Sk16f; | 
|---|
| 393 |  | 
|---|
| 394 | typedef SkNx<2,  SkScalar> Sk2s; | 
|---|
| 395 | typedef SkNx<4,  SkScalar> Sk4s; | 
|---|
| 396 | typedef SkNx<8,  SkScalar> Sk8s; | 
|---|
| 397 | typedef SkNx<16, SkScalar> Sk16s; | 
|---|
| 398 |  | 
|---|
| 399 | typedef SkNx<4,   uint8_t> Sk4b; | 
|---|
| 400 | typedef SkNx<8,   uint8_t> Sk8b; | 
|---|
| 401 | typedef SkNx<16,  uint8_t> Sk16b; | 
|---|
| 402 |  | 
|---|
| 403 | typedef SkNx<4,  uint16_t> Sk4h; | 
|---|
| 404 | typedef SkNx<8,  uint16_t> Sk8h; | 
|---|
| 405 | typedef SkNx<16, uint16_t> Sk16h; | 
|---|
| 406 |  | 
|---|
| 407 | typedef SkNx<4,  int32_t> Sk4i; | 
|---|
| 408 | typedef SkNx<8,  int32_t> Sk8i; | 
|---|
| 409 | typedef SkNx<4, uint32_t> Sk4u; | 
|---|
| 410 |  | 
|---|
| 411 | // Include platform specific specializations if available. | 
|---|
| 412 | #if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 | 
|---|
| 413 | #include "include/private/SkNx_sse.h" | 
|---|
| 414 | #elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON) | 
|---|
| 415 | #include "include/private/SkNx_neon.h" | 
|---|
| 416 | #else | 
|---|
| 417 |  | 
|---|
| 418 | AI static Sk4i Sk4f_round(const Sk4f& x) { | 
|---|
| 419 | return { (int) lrintf (x[0]), | 
|---|
| 420 | (int) lrintf (x[1]), | 
|---|
| 421 | (int) lrintf (x[2]), | 
|---|
| 422 | (int) lrintf (x[3]), }; | 
|---|
| 423 | } | 
|---|
| 424 |  | 
|---|
| 425 | #endif | 
|---|
| 426 |  | 
|---|
| 427 | AI static void Sk4f_ToBytes(uint8_t p[16], | 
|---|
| 428 | const Sk4f& a, const Sk4f& b, const Sk4f& c, const Sk4f& d) { | 
|---|
| 429 | SkNx_cast<uint8_t>(SkNx_join(SkNx_join(a,b), SkNx_join(c,d))).store(p); | 
|---|
| 430 | } | 
|---|
| 431 |  | 
|---|
| 432 | #undef AI | 
|---|
| 433 |  | 
|---|
| 434 | #endif//SkNx_DEFINED | 
|---|
| 435 |  | 
|---|