1 /*
2 * Copyright 2008 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 // The copyright below was added in 2009, but I see no record of moto contributions...?
9
10 /* NEON optimized code (C) COPYRIGHT 2009 Motorola
11 *
12 * Use of this source code is governed by a BSD-style license that can be
13 * found in the LICENSE file.
14 */
15
16 #include "SkBitmapProcState.h"
17 #include "SkShader.h"
18 #include "SkUtils.h"
19 #include "SkUtilsArm.h"
20 #include "SkBitmapProcState_utils.h"
21
22 /* returns 0...(n-1) given any x (positive or negative).
23
24 As an example, if n (which is always positive) is 5...
25
26 x: -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8
27 returns: 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3
28 */
sk_int_mod(int x,int n)29 static inline int sk_int_mod(int x, int n) {
30 SkASSERT(n > 0);
31 if ((unsigned)x >= (unsigned)n) {
32 if (x < 0) {
33 x = n + ~(~x % n);
34 } else {
35 x = x % n;
36 }
37 }
38 return x;
39 }
40
41 void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
42 void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
43
44 #include "SkBitmapProcState_matrix_template.h"
45
46 ///////////////////////////////////////////////////////////////////////////////
47
48 // Compile neon code paths if needed
49 #if defined(SK_ARM_HAS_NEON)
50
51 // These are defined in src/opts/SkBitmapProcState_matrixProcs_neon.cpp
52 extern const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs_neon[];
53 extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[];
54
55 #endif // defined(SK_ARM_HAS_NEON)
56
57 // Compile non-neon code path if needed
58 #if !defined(SK_ARM_HAS_NEON)
59 #define MAKENAME(suffix) ClampX_ClampY ## suffix
60 #define TILEX_PROCF(fx, max) SkClampMax((fx) >> 16, max)
61 #define TILEY_PROCF(fy, max) SkClampMax((fy) >> 16, max)
62 #define EXTRACT_LOW_BITS(v, max) (((v) >> 12) & 0xF)
63 #define CHECK_FOR_DECAL
64 #include "SkBitmapProcState_matrix.h"
65
66 struct ClampTileProcs {
XClampTileProcs67 static unsigned X(const SkBitmapProcState&, SkFixed fx, int max) {
68 return SkClampMax(fx >> 16, max);
69 }
YClampTileProcs70 static unsigned Y(const SkBitmapProcState&, SkFixed fy, int max) {
71 return SkClampMax(fy >> 16, max);
72 }
73 };
74
75 // Referenced in opts_check_x86.cpp
ClampX_ClampY_nofilter_scale(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)76 void ClampX_ClampY_nofilter_scale(const SkBitmapProcState& s, uint32_t xy[],
77 int count, int x, int y) {
78 return NoFilterProc_Scale<ClampTileProcs, true>(s, xy, count, x, y);
79 }
ClampX_ClampY_nofilter_affine(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)80 void ClampX_ClampY_nofilter_affine(const SkBitmapProcState& s, uint32_t xy[],
81 int count, int x, int y) {
82 return NoFilterProc_Affine<ClampTileProcs>(s, xy, count, x, y);
83 }
84
85 static SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = {
86 // only clamp lives in the right coord space to check for decal
87 ClampX_ClampY_nofilter_scale,
88 ClampX_ClampY_filter_scale,
89 ClampX_ClampY_nofilter_affine,
90 ClampX_ClampY_filter_affine,
91 };
92
93 #define MAKENAME(suffix) RepeatX_RepeatY ## suffix
94 #define TILEX_PROCF(fx, max) SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1))
95 #define TILEY_PROCF(fy, max) SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1))
96 #define EXTRACT_LOW_BITS(v, max) (((unsigned)((v) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
97 #include "SkBitmapProcState_matrix.h"
98
99 struct RepeatTileProcs {
XRepeatTileProcs100 static unsigned X(const SkBitmapProcState&, SkFixed fx, int max) {
101 SkASSERT(max < 65535);
102 return SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1));
103 }
YRepeatTileProcs104 static unsigned Y(const SkBitmapProcState&, SkFixed fy, int max) {
105 SkASSERT(max < 65535);
106 return SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1));
107 }
108 };
109
110 static SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs[] = {
111 NoFilterProc_Scale<RepeatTileProcs, false>,
112 RepeatX_RepeatY_filter_scale,
113 NoFilterProc_Affine<RepeatTileProcs>,
114 RepeatX_RepeatY_filter_affine,
115 };
116 #endif
117
118 #define MAKENAME(suffix) GeneralXY ## suffix
119 #define PREAMBLE(state) SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; (void) tileProcX; \
120 SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; (void) tileProcY;
121 #define PREAMBLE_PARAM_X , SkBitmapProcState::FixedTileProc tileProcX
122 #define PREAMBLE_PARAM_Y , SkBitmapProcState::FixedTileProc tileProcY
123 #define PREAMBLE_ARG_X , tileProcX
124 #define PREAMBLE_ARG_Y , tileProcY
125 #define TILEX_PROCF(fx, max) SK_USHIFT16(tileProcX(fx) * ((max) + 1))
126 #define TILEY_PROCF(fy, max) SK_USHIFT16(tileProcY(fy) * ((max) + 1))
127 #define EXTRACT_LOW_BITS(v, max) (((v * (max + 1)) >> 12) & 0xF)
128 #include "SkBitmapProcState_matrix.h"
129
130 struct GeneralTileProcs {
XGeneralTileProcs131 static unsigned X(const SkBitmapProcState& s, SkFixed fx, int max) {
132 return SK_USHIFT16(s.fTileProcX(fx) * ((max) + 1));
133 }
YGeneralTileProcs134 static unsigned Y(const SkBitmapProcState& s, SkFixed fy, int max) {
135 return SK_USHIFT16(s.fTileProcY(fy) * ((max) + 1));
136 }
137 };
138
139 static SkBitmapProcState::MatrixProc GeneralXY_Procs[] = {
140 NoFilterProc_Scale<GeneralTileProcs, false>,
141 GeneralXY_filter_scale,
142 NoFilterProc_Affine<GeneralTileProcs>,
143 GeneralXY_filter_affine,
144 };
145
146 ///////////////////////////////////////////////////////////////////////////////
147
fixed_clamp(SkFixed x)148 static inline U16CPU fixed_clamp(SkFixed x) {
149 if (x < 0) {
150 x = 0;
151 }
152 if (x >> 16) {
153 x = 0xFFFF;
154 }
155 return x;
156 }
157
fixed_repeat(SkFixed x)158 static inline U16CPU fixed_repeat(SkFixed x) {
159 return x & 0xFFFF;
160 }
161
fixed_mirror(SkFixed x)162 static inline U16CPU fixed_mirror(SkFixed x) {
163 SkFixed s = SkLeftShift(x, 15) >> 31;
164 // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
165 return (x ^ s) & 0xFFFF;
166 }
167
choose_tile_proc(unsigned m)168 static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m) {
169 if (SkShader::kClamp_TileMode == m) {
170 return fixed_clamp;
171 }
172 if (SkShader::kRepeat_TileMode == m) {
173 return fixed_repeat;
174 }
175 SkASSERT(SkShader::kMirror_TileMode == m);
176 return fixed_mirror;
177 }
178
int_clamp(int x,int n)179 static inline U16CPU int_clamp(int x, int n) {
180 if (x >= n) {
181 x = n - 1;
182 }
183 if (x < 0) {
184 x = 0;
185 }
186 return x;
187 }
188
int_repeat(int x,int n)189 static inline U16CPU int_repeat(int x, int n) {
190 return sk_int_mod(x, n);
191 }
192
int_mirror(int x,int n)193 static inline U16CPU int_mirror(int x, int n) {
194 x = sk_int_mod(x, 2 * n);
195 if (x >= n) {
196 x = n + ~(x - n);
197 }
198 return x;
199 }
200
201 #if 0
202 static void test_int_tileprocs() {
203 for (int i = -8; i <= 8; i++) {
204 SkDebugf(" int_mirror(%2d, 3) = %d\n", i, int_mirror(i, 3));
205 }
206 }
207 #endif
208
choose_int_tile_proc(unsigned tm)209 static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned tm) {
210 if (SkShader::kClamp_TileMode == tm)
211 return int_clamp;
212 if (SkShader::kRepeat_TileMode == tm)
213 return int_repeat;
214 SkASSERT(SkShader::kMirror_TileMode == tm);
215 return int_mirror;
216 }
217
218 //////////////////////////////////////////////////////////////////////////////
219
decal_nofilter_scale(uint32_t dst[],SkFixed fx,SkFixed dx,int count)220 void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) {
221 int i;
222
223 for (i = (count >> 2); i > 0; --i) {
224 *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
225 fx += dx+dx;
226 *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
227 fx += dx+dx;
228 }
229 count &= 3;
230
231 uint16_t* xx = (uint16_t*)dst;
232 for (i = count; i > 0; --i) {
233 *xx++ = SkToU16(fx >> 16); fx += dx;
234 }
235 }
236
decal_filter_scale(uint32_t dst[],SkFixed fx,SkFixed dx,int count)237 void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) {
238 if (count & 1) {
239 SkASSERT((fx >> (16 + 14)) == 0);
240 *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
241 fx += dx;
242 }
243 while ((count -= 2) >= 0) {
244 SkASSERT((fx >> (16 + 14)) == 0);
245 *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
246 fx += dx;
247
248 *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
249 fx += dx;
250 }
251 }
252
253 ///////////////////////////////////////////////////////////////////////////////
254 // stores the same as SCALE, but is cheaper to compute. Also since there is no
255 // scale, we don't need/have a FILTER version
256
fill_sequential(uint16_t xptr[],int start,int count)257 static void fill_sequential(uint16_t xptr[], int start, int count) {
258 #if 1
259 if (reinterpret_cast<intptr_t>(xptr) & 0x2) {
260 *xptr++ = start++;
261 count -= 1;
262 }
263 if (count > 3) {
264 uint32_t* xxptr = reinterpret_cast<uint32_t*>(xptr);
265 uint32_t pattern0 = PACK_TWO_SHORTS(start + 0, start + 1);
266 uint32_t pattern1 = PACK_TWO_SHORTS(start + 2, start + 3);
267 start += count & ~3;
268 int qcount = count >> 2;
269 do {
270 *xxptr++ = pattern0;
271 pattern0 += 0x40004;
272 *xxptr++ = pattern1;
273 pattern1 += 0x40004;
274 } while (--qcount != 0);
275 xptr = reinterpret_cast<uint16_t*>(xxptr);
276 count &= 3;
277 }
278 while (--count >= 0) {
279 *xptr++ = start++;
280 }
281 #else
282 for (int i = 0; i < count; i++) {
283 *xptr++ = start++;
284 }
285 #endif
286 }
287
nofilter_trans_preamble(const SkBitmapProcState & s,uint32_t ** xy,int x,int y)288 static int nofilter_trans_preamble(const SkBitmapProcState& s, uint32_t** xy,
289 int x, int y) {
290 const SkBitmapProcStateAutoMapper mapper(s, x, y);
291 **xy = s.fIntTileProcY(mapper.intY(), s.fPixmap.height());
292 *xy += 1; // bump the ptr
293 // return our starting X position
294 return mapper.intX();
295 }
296
clampx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)297 static void clampx_nofilter_trans(const SkBitmapProcState& s,
298 uint32_t xy[], int count, int x, int y) {
299 SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
300
301 int xpos = nofilter_trans_preamble(s, &xy, x, y);
302 const int width = s.fPixmap.width();
303 if (1 == width) {
304 // all of the following X values must be 0
305 memset(xy, 0, count * sizeof(uint16_t));
306 return;
307 }
308
309 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
310 int n;
311
312 // fill before 0 as needed
313 if (xpos < 0) {
314 n = -xpos;
315 if (n > count) {
316 n = count;
317 }
318 memset(xptr, 0, n * sizeof(uint16_t));
319 count -= n;
320 if (0 == count) {
321 return;
322 }
323 xptr += n;
324 xpos = 0;
325 }
326
327 // fill in 0..width-1 if needed
328 if (xpos < width) {
329 n = width - xpos;
330 if (n > count) {
331 n = count;
332 }
333 fill_sequential(xptr, xpos, n);
334 count -= n;
335 if (0 == count) {
336 return;
337 }
338 xptr += n;
339 }
340
341 // fill the remaining with the max value
342 sk_memset16(xptr, width - 1, count);
343 }
344
repeatx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)345 static void repeatx_nofilter_trans(const SkBitmapProcState& s,
346 uint32_t xy[], int count, int x, int y) {
347 SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
348
349 int xpos = nofilter_trans_preamble(s, &xy, x, y);
350 const int width = s.fPixmap.width();
351 if (1 == width) {
352 // all of the following X values must be 0
353 memset(xy, 0, count * sizeof(uint16_t));
354 return;
355 }
356
357 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
358 int start = sk_int_mod(xpos, width);
359 int n = width - start;
360 if (n > count) {
361 n = count;
362 }
363 fill_sequential(xptr, start, n);
364 xptr += n;
365 count -= n;
366
367 while (count >= width) {
368 fill_sequential(xptr, 0, width);
369 xptr += width;
370 count -= width;
371 }
372
373 if (count > 0) {
374 fill_sequential(xptr, 0, count);
375 }
376 }
377
fill_backwards(uint16_t xptr[],int pos,int count)378 static void fill_backwards(uint16_t xptr[], int pos, int count) {
379 for (int i = 0; i < count; i++) {
380 SkASSERT(pos >= 0);
381 xptr[i] = pos--;
382 }
383 }
384
mirrorx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)385 static void mirrorx_nofilter_trans(const SkBitmapProcState& s,
386 uint32_t xy[], int count, int x, int y) {
387 SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
388
389 int xpos = nofilter_trans_preamble(s, &xy, x, y);
390 const int width = s.fPixmap.width();
391 if (1 == width) {
392 // all of the following X values must be 0
393 memset(xy, 0, count * sizeof(uint16_t));
394 return;
395 }
396
397 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
398 // need to know our start, and our initial phase (forward or backward)
399 bool forward;
400 int n;
401 int start = sk_int_mod(xpos, 2 * width);
402 if (start >= width) {
403 start = width + ~(start - width);
404 forward = false;
405 n = start + 1; // [start .. 0]
406 } else {
407 forward = true;
408 n = width - start; // [start .. width)
409 }
410 if (n > count) {
411 n = count;
412 }
413 if (forward) {
414 fill_sequential(xptr, start, n);
415 } else {
416 fill_backwards(xptr, start, n);
417 }
418 forward = !forward;
419 xptr += n;
420 count -= n;
421
422 while (count >= width) {
423 if (forward) {
424 fill_sequential(xptr, 0, width);
425 } else {
426 fill_backwards(xptr, width - 1, width);
427 }
428 forward = !forward;
429 xptr += width;
430 count -= width;
431 }
432
433 if (count > 0) {
434 if (forward) {
435 fill_sequential(xptr, 0, count);
436 } else {
437 fill_backwards(xptr, width - 1, count);
438 }
439 }
440 }
441
442 ///////////////////////////////////////////////////////////////////////////////
443
chooseMatrixProc(bool trivial_matrix)444 SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
445 SkASSERT((fInvType & SkMatrix::kPerspective_Mask) == 0);
446
447 // test_int_tileprocs();
448 // check for our special case when there is no scale/affine/perspective
449 if (trivial_matrix && kNone_SkFilterQuality == fFilterQuality) {
450 fIntTileProcY = choose_int_tile_proc(fTileModeY);
451 switch (fTileModeX) {
452 case SkShader::kClamp_TileMode:
453 return clampx_nofilter_trans;
454 case SkShader::kRepeat_TileMode:
455 return repeatx_nofilter_trans;
456 case SkShader::kMirror_TileMode:
457 return mirrorx_nofilter_trans;
458 }
459 }
460
461 int index = 0;
462 if (fFilterQuality != kNone_SkFilterQuality) {
463 index = 1;
464 }
465 if (fInvType & SkMatrix::kAffine_Mask) {
466 index += 2;
467 }
468
469 if (SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY) {
470 // clamp gets special version of filterOne
471 fFilterOneX = SK_Fixed1;
472 fFilterOneY = SK_Fixed1;
473 return SK_ARM_NEON_WRAP(ClampX_ClampY_Procs)[index];
474 }
475
476 // all remaining procs use this form for filterOne
477 fFilterOneX = SK_Fixed1 / fPixmap.width();
478 fFilterOneY = SK_Fixed1 / fPixmap.height();
479
480 if (SkShader::kRepeat_TileMode == fTileModeX && SkShader::kRepeat_TileMode == fTileModeY) {
481 return SK_ARM_NEON_WRAP(RepeatX_RepeatY_Procs)[index];
482 }
483
484 fTileProcX = choose_tile_proc(fTileModeX);
485 fTileProcY = choose_tile_proc(fTileModeY);
486 return GeneralXY_Procs[index];
487 }
488