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 }
80 
81 static SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = {
82     // only clamp lives in the right coord space to check for decal
83     ClampX_ClampY_nofilter_scale,
84     ClampX_ClampY_filter_scale,
85 };
86 
87 #define MAKENAME(suffix)         RepeatX_RepeatY ## suffix
88 #define TILEX_PROCF(fx, max)     SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1))
89 #define TILEY_PROCF(fy, max)     SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1))
90 #define EXTRACT_LOW_BITS(v, max) (((unsigned)((v) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
91 #include "SkBitmapProcState_matrix.h"
92 
93 struct RepeatTileProcs {
XRepeatTileProcs94     static unsigned X(const SkBitmapProcState&, SkFixed fx, int max) {
95         SkASSERT(max < 65535);
96         return SK_USHIFT16((unsigned)((fx) & 0xFFFF) * ((max) + 1));
97     }
YRepeatTileProcs98     static unsigned Y(const SkBitmapProcState&, SkFixed fy, int max) {
99         SkASSERT(max < 65535);
100         return SK_USHIFT16((unsigned)((fy) & 0xFFFF) * ((max) + 1));
101     }
102 };
103 
104 static SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs[] = {
105     NoFilterProc_Scale<RepeatTileProcs, false>,
106     RepeatX_RepeatY_filter_scale,
107 };
108 #endif
109 
110 #define MAKENAME(suffix)        GeneralXY ## suffix
111 #define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; (void) tileProcX; \
112                                 SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; (void) tileProcY;
113 #define PREAMBLE_PARAM_X        , SkBitmapProcState::FixedTileProc tileProcX
114 #define PREAMBLE_PARAM_Y        , SkBitmapProcState::FixedTileProc tileProcY
115 #define PREAMBLE_ARG_X          , tileProcX
116 #define PREAMBLE_ARG_Y          , tileProcY
117 #define TILEX_PROCF(fx, max)    SK_USHIFT16(tileProcX(fx) * ((max) + 1))
118 #define TILEY_PROCF(fy, max)    SK_USHIFT16(tileProcY(fy) * ((max) + 1))
119 #define EXTRACT_LOW_BITS(v, max) (((v * (max + 1)) >> 12) & 0xF)
120 #include "SkBitmapProcState_matrix.h"
121 
122 struct GeneralTileProcs {
XGeneralTileProcs123     static unsigned X(const SkBitmapProcState& s, SkFixed fx, int max) {
124         return SK_USHIFT16(s.fTileProcX(fx) * ((max) + 1));
125     }
YGeneralTileProcs126     static unsigned Y(const SkBitmapProcState& s, SkFixed fy, int max) {
127         return SK_USHIFT16(s.fTileProcY(fy) * ((max) + 1));
128     }
129 };
130 
131 static SkBitmapProcState::MatrixProc GeneralXY_Procs[] = {
132     NoFilterProc_Scale<GeneralTileProcs, false>,
133     GeneralXY_filter_scale,
134 };
135 
136 ///////////////////////////////////////////////////////////////////////////////
137 
fixed_clamp(SkFixed x)138 static inline U16CPU fixed_clamp(SkFixed x) {
139     if (x < 0) {
140         x = 0;
141     }
142     if (x >> 16) {
143         x = 0xFFFF;
144     }
145     return x;
146 }
147 
fixed_repeat(SkFixed x)148 static inline U16CPU fixed_repeat(SkFixed x) {
149     return x & 0xFFFF;
150 }
151 
fixed_mirror(SkFixed x)152 static inline U16CPU fixed_mirror(SkFixed x) {
153     SkFixed s = SkLeftShift(x, 15) >> 31;
154     // s is FFFFFFFF if we're on an odd interval, or 0 if an even interval
155     return (x ^ s) & 0xFFFF;
156 }
157 
choose_tile_proc(unsigned m)158 static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m) {
159     if (SkShader::kClamp_TileMode == m) {
160         return fixed_clamp;
161     }
162     if (SkShader::kRepeat_TileMode == m) {
163         return fixed_repeat;
164     }
165     SkASSERT(SkShader::kMirror_TileMode == m);
166     return fixed_mirror;
167 }
168 
int_clamp(int x,int n)169 static inline U16CPU int_clamp(int x, int n) {
170     if (x >= n) {
171         x = n - 1;
172     }
173     if (x < 0) {
174         x = 0;
175     }
176     return x;
177 }
178 
int_repeat(int x,int n)179 static inline U16CPU int_repeat(int x, int n) {
180     return sk_int_mod(x, n);
181 }
182 
int_mirror(int x,int n)183 static inline U16CPU int_mirror(int x, int n) {
184     x = sk_int_mod(x, 2 * n);
185     if (x >= n) {
186         x = n + ~(x - n);
187     }
188     return x;
189 }
190 
191 #if 0
192 static void test_int_tileprocs() {
193     for (int i = -8; i <= 8; i++) {
194         SkDebugf(" int_mirror(%2d, 3) = %d\n", i, int_mirror(i, 3));
195     }
196 }
197 #endif
198 
choose_int_tile_proc(unsigned tm)199 static SkBitmapProcState::IntTileProc choose_int_tile_proc(unsigned tm) {
200     if (SkShader::kClamp_TileMode == tm)
201         return int_clamp;
202     if (SkShader::kRepeat_TileMode == tm)
203         return int_repeat;
204     SkASSERT(SkShader::kMirror_TileMode == tm);
205     return int_mirror;
206 }
207 
208 //////////////////////////////////////////////////////////////////////////////
209 
decal_nofilter_scale(uint32_t dst[],SkFixed fx,SkFixed dx,int count)210 void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) {
211     int i;
212 
213     for (i = (count >> 2); i > 0; --i) {
214         *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
215         fx += dx+dx;
216         *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
217         fx += dx+dx;
218     }
219     count &= 3;
220 
221     uint16_t* xx = (uint16_t*)dst;
222     for (i = count; i > 0; --i) {
223         *xx++ = SkToU16(fx >> 16); fx += dx;
224     }
225 }
226 
decal_filter_scale(uint32_t dst[],SkFixed fx,SkFixed dx,int count)227 void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) {
228     if (count & 1) {
229         SkASSERT((fx >> (16 + 14)) == 0);
230         *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
231         fx += dx;
232     }
233     while ((count -= 2) >= 0) {
234         SkASSERT((fx >> (16 + 14)) == 0);
235         *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
236         fx += dx;
237 
238         *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
239         fx += dx;
240     }
241 }
242 
243 ///////////////////////////////////////////////////////////////////////////////
244 // stores the same as SCALE, but is cheaper to compute. Also since there is no
245 // scale, we don't need/have a FILTER version
246 
fill_sequential(uint16_t xptr[],int start,int count)247 static void fill_sequential(uint16_t xptr[], int start, int count) {
248 #if 1
249     if (reinterpret_cast<intptr_t>(xptr) & 0x2) {
250         *xptr++ = start++;
251         count -= 1;
252     }
253     if (count > 3) {
254         uint32_t* xxptr = reinterpret_cast<uint32_t*>(xptr);
255         uint32_t pattern0 = PACK_TWO_SHORTS(start + 0, start + 1);
256         uint32_t pattern1 = PACK_TWO_SHORTS(start + 2, start + 3);
257         start += count & ~3;
258         int qcount = count >> 2;
259         do {
260             *xxptr++ = pattern0;
261             pattern0 += 0x40004;
262             *xxptr++ = pattern1;
263             pattern1 += 0x40004;
264         } while (--qcount != 0);
265         xptr = reinterpret_cast<uint16_t*>(xxptr);
266         count &= 3;
267     }
268     while (--count >= 0) {
269         *xptr++ = start++;
270     }
271 #else
272     for (int i = 0; i < count; i++) {
273         *xptr++ = start++;
274     }
275 #endif
276 }
277 
nofilter_trans_preamble(const SkBitmapProcState & s,uint32_t ** xy,int x,int y)278 static int nofilter_trans_preamble(const SkBitmapProcState& s, uint32_t** xy,
279                                    int x, int y) {
280     const SkBitmapProcStateAutoMapper mapper(s, x, y);
281     **xy = s.fIntTileProcY(mapper.intY(), s.fPixmap.height());
282     *xy += 1;   // bump the ptr
283     // return our starting X position
284     return mapper.intX();
285 }
286 
clampx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)287 static void clampx_nofilter_trans(const SkBitmapProcState& s,
288                                   uint32_t xy[], int count, int x, int y) {
289     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
290 
291     int xpos = nofilter_trans_preamble(s, &xy, x, y);
292     const int width = s.fPixmap.width();
293     if (1 == width) {
294         // all of the following X values must be 0
295         memset(xy, 0, count * sizeof(uint16_t));
296         return;
297     }
298 
299     uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
300     int n;
301 
302     // fill before 0 as needed
303     if (xpos < 0) {
304         n = -xpos;
305         if (n > count) {
306             n = count;
307         }
308         memset(xptr, 0, n * sizeof(uint16_t));
309         count -= n;
310         if (0 == count) {
311             return;
312         }
313         xptr += n;
314         xpos = 0;
315     }
316 
317     // fill in 0..width-1 if needed
318     if (xpos < width) {
319         n = width - xpos;
320         if (n > count) {
321             n = count;
322         }
323         fill_sequential(xptr, xpos, n);
324         count -= n;
325         if (0 == count) {
326             return;
327         }
328         xptr += n;
329     }
330 
331     // fill the remaining with the max value
332     sk_memset16(xptr, width - 1, count);
333 }
334 
repeatx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)335 static void repeatx_nofilter_trans(const SkBitmapProcState& s,
336                                    uint32_t xy[], int count, int x, int y) {
337     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
338 
339     int xpos = nofilter_trans_preamble(s, &xy, x, y);
340     const int width = s.fPixmap.width();
341     if (1 == width) {
342         // all of the following X values must be 0
343         memset(xy, 0, count * sizeof(uint16_t));
344         return;
345     }
346 
347     uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
348     int start = sk_int_mod(xpos, width);
349     int n = width - start;
350     if (n > count) {
351         n = count;
352     }
353     fill_sequential(xptr, start, n);
354     xptr += n;
355     count -= n;
356 
357     while (count >= width) {
358         fill_sequential(xptr, 0, width);
359         xptr += width;
360         count -= width;
361     }
362 
363     if (count > 0) {
364         fill_sequential(xptr, 0, count);
365     }
366 }
367 
fill_backwards(uint16_t xptr[],int pos,int count)368 static void fill_backwards(uint16_t xptr[], int pos, int count) {
369     for (int i = 0; i < count; i++) {
370         SkASSERT(pos >= 0);
371         xptr[i] = pos--;
372     }
373 }
374 
mirrorx_nofilter_trans(const SkBitmapProcState & s,uint32_t xy[],int count,int x,int y)375 static void mirrorx_nofilter_trans(const SkBitmapProcState& s,
376                                    uint32_t xy[], int count, int x, int y) {
377     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
378 
379     int xpos = nofilter_trans_preamble(s, &xy, x, y);
380     const int width = s.fPixmap.width();
381     if (1 == width) {
382         // all of the following X values must be 0
383         memset(xy, 0, count * sizeof(uint16_t));
384         return;
385     }
386 
387     uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
388     // need to know our start, and our initial phase (forward or backward)
389     bool forward;
390     int n;
391     int start = sk_int_mod(xpos, 2 * width);
392     if (start >= width) {
393         start = width + ~(start - width);
394         forward = false;
395         n = start + 1;  // [start .. 0]
396     } else {
397         forward = true;
398         n = width - start;  // [start .. width)
399     }
400     if (n > count) {
401         n = count;
402     }
403     if (forward) {
404         fill_sequential(xptr, start, n);
405     } else {
406         fill_backwards(xptr, start, n);
407     }
408     forward = !forward;
409     xptr += n;
410     count -= n;
411 
412     while (count >= width) {
413         if (forward) {
414             fill_sequential(xptr, 0, width);
415         } else {
416             fill_backwards(xptr, width - 1, width);
417         }
418         forward = !forward;
419         xptr += width;
420         count -= width;
421     }
422 
423     if (count > 0) {
424         if (forward) {
425             fill_sequential(xptr, 0, count);
426         } else {
427             fill_backwards(xptr, width - 1, count);
428         }
429     }
430 }
431 
432 ///////////////////////////////////////////////////////////////////////////////
433 
chooseMatrixProc(bool trivial_matrix)434 SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool trivial_matrix) {
435     SkASSERT((fInvType & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) == 0);
436 
437 //    test_int_tileprocs();
438     // check for our special case when there is no scale/affine/perspective
439     if (trivial_matrix && kNone_SkFilterQuality == fFilterQuality) {
440         fIntTileProcY = choose_int_tile_proc(fTileModeY);
441         switch (fTileModeX) {
442             case SkShader::kClamp_TileMode:
443                 return clampx_nofilter_trans;
444             case SkShader::kRepeat_TileMode:
445                 return repeatx_nofilter_trans;
446             case SkShader::kMirror_TileMode:
447                 return mirrorx_nofilter_trans;
448             case SkShader::kDecal_TileMode:
449                 SkASSERT(false);    // should never get here, handled by stages
450                 return clampx_nofilter_trans;
451         }
452     }
453 
454     int index = 0;
455     if (fFilterQuality != kNone_SkFilterQuality) {
456         index = 1;
457     }
458 
459     if (SkShader::kClamp_TileMode == fTileModeX && SkShader::kClamp_TileMode == fTileModeY) {
460         // clamp gets special version of filterOne
461         fFilterOneX = SK_Fixed1;
462         fFilterOneY = SK_Fixed1;
463         return SK_ARM_NEON_WRAP(ClampX_ClampY_Procs)[index];
464     }
465 
466     // all remaining procs use this form for filterOne
467     fFilterOneX = SK_Fixed1 / fPixmap.width();
468     fFilterOneY = SK_Fixed1 / fPixmap.height();
469 
470     if (SkShader::kRepeat_TileMode == fTileModeX && SkShader::kRepeat_TileMode == fTileModeY) {
471         return SK_ARM_NEON_WRAP(RepeatX_RepeatY_Procs)[index];
472     }
473 
474     fTileProcX = choose_tile_proc(fTileModeX);
475     fTileProcY = choose_tile_proc(fTileModeY);
476     return GeneralXY_Procs[index];
477 }
478