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 | |