SkNx.h 17 KB

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