Home | History | Annotate | Download | only in core
      1 /* NEON optimized code (C) COPYRIGHT 2009 Motorola
      2  *
      3  * Use of this source code is governed by a BSD-style license that can be
      4  * found in the LICENSE file.
      5  */
      6 
      7 /*
      8  * Modifications done in-house at Motorola
      9  *
     10  * this is a clone of SkBitmapProcState_matrix.h
     11  * and has been tuned to work with the NEON unit.
     12  *
     13  * Still going back and forth between whether this approach
     14  * (clone the entire SkBitmapProcState_matrix.h file or
     15  * if I should put just the modified routines in here and
     16  * then use a construct like #define DONT_DO_THIS_FUNCTION or
     17  * something like that...
     18  *
     19  * This is for the ClampX_ClampY instance
     20  *
     21  */
     22 
     23 
     24 #if	!defined(__ARM_HAVE_NEON)
     25 #error	this file can be used only when the NEON unit is enabled
     26 #endif
     27 
     28 #include <arm_neon.h>
     29 
     30 /*
     31  * This has been modified on the knowledge that (at the time)
     32  * we had the following macro definitions in the parent file
     33  *
     34  * #define MAKENAME(suffix)        ClampX_ClampY ## suffix
     35  * #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
     36  * #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
     37  * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
     38  * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
     39  * #define CHECK_FOR_DECAL
     40  */
     41 
     42 /* SkClampMax(val,max) -- bound to 0..max */
     43 
     44 #define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale_neon)
     45 #define SCALE_FILTER_NAME       MAKENAME(_filter_scale_neon)
     46 #define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine_neon)
     47 #define AFFINE_FILTER_NAME      MAKENAME(_filter_affine_neon)
     48 #define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp_neon)
     49 #define PERSP_FILTER_NAME       MAKENAME(_filter_persp_neon)
     50 
     51 #define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
     52 #define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
     53 
     54 #ifndef PREAMBLE
     55     #define PREAMBLE(state)
     56     #define PREAMBLE_PARAM_X
     57     #define PREAMBLE_PARAM_Y
     58     #define PREAMBLE_ARG_X
     59     #define PREAMBLE_ARG_Y
     60 #endif
     61 
     62 static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
     63                                 uint32_t xy[], int count, int x, int y) {
     64     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
     65                              SkMatrix::kScale_Mask)) == 0);
     66 
     67     PREAMBLE(s);
     68     // we store y, x, x, x, x, x
     69 
     70     const unsigned maxX = s.fBitmap->width() - 1;
     71     SkFixed fx;
     72     {
     73         SkPoint pt;
     74         s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
     75                                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
     76         fx = SkScalarToFixed(pt.fY);
     77         const unsigned maxY = s.fBitmap->height() - 1;
     78         *xy++ = TILEY_PROCF(fx, maxY);
     79         fx = SkScalarToFixed(pt.fX);
     80     }
     81 
     82     if (0 == maxX) {
     83         // all of the following X values must be 0
     84         memset(xy, 0, count * sizeof(uint16_t));
     85         return;
     86     }
     87 
     88     const SkFixed dx = s.fInvSx;
     89 
     90 #ifdef CHECK_FOR_DECAL
     91     // test if we don't need to apply the tile proc
     92     if ((unsigned)(fx >> 16) <= maxX &&
     93         (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
     94         decal_nofilter_scale(xy, fx, dx, count);
     95         return;
     96     }
     97 #endif
     98 
     99     int i;
    100 
    101 	/* very much like done in decal_nofilter, but with
    102 	 * an extra clamping function applied.
    103 	 * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max)
    104 	 */
    105 	if (count >= 8) {
    106 	    /* SkFixed is 16.16 fixed point */
    107 	    SkFixed dx2 = dx+dx;
    108 	    SkFixed dx4 = dx2+dx2;
    109 	    SkFixed dx8 = dx4+dx4;
    110 
    111 	    /* now build fx/fx+dx/fx+2dx/fx+3dx */
    112 	    SkFixed fx1, fx2, fx3;
    113 	    int32x2_t lower, upper;
    114 	    int32x4_t lbase, hbase;
    115 	    int16_t *dst16 = (int16_t *)xy;
    116 
    117 	    fx1 = fx+dx;
    118 	    fx2 = fx1+dx;
    119 	    fx3 = fx2+dx;
    120 
    121 	    /* build my template(s) */
    122 	    /* avoid the 'lbase unitialized' warning */
    123 	    lbase = vdupq_n_s32(fx);
    124 	    lbase = vsetq_lane_s32(fx1, lbase, 1);
    125 	    lbase = vsetq_lane_s32(fx2, lbase, 2);
    126 	    lbase = vsetq_lane_s32(fx3, lbase, 3);
    127 
    128 	    hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
    129 
    130 	    /* store & bump */
    131 	    do {
    132 	        int32x4_t lout;
    133             int32x4_t hout;
    134             int16x8_t hi16;
    135 
    136             /* get the hi 16s of all those 32s */
    137             lout = lbase;
    138             hout = hbase;
    139             /* this sets up all lout's then all hout's in hout */
    140             asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
    141             hi16 = vreinterpretq_s16_s32(hout);
    142 
    143             /* clamp & output */
    144             hi16 = vmaxq_s16(hi16, vdupq_n_s16(0));
    145             hi16 = vminq_s16(hi16, vdupq_n_s16(maxX));
    146             vst1q_s16(dst16, hi16);
    147 
    148             /* but preserving base & on to the next */
    149             lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
    150             hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
    151             dst16 += 8;
    152             count -= 8;
    153             fx += dx8;
    154 	    } while (count >= 8);
    155 	    xy = (uint32_t *) dst16;
    156 	}
    157 
    158     uint16_t* xx = (uint16_t*)xy;
    159     for (i = count; i > 0; --i) {
    160         *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
    161     }
    162 }
    163 
    164 // note: we could special-case on a matrix which is skewed in X but not Y.
    165 // this would require a more general setup thatn SCALE does, but could use
    166 // SCALE's inner loop that only looks at dx
    167 
    168 static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
    169                                  uint32_t xy[], int count, int x, int y) {
    170     SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    171     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
    172                              SkMatrix::kScale_Mask |
    173                              SkMatrix::kAffine_Mask)) == 0);
    174 
    175     PREAMBLE(s);
    176     SkPoint srcPt;
    177     s.fInvProc(*s.fInvMatrix,
    178                SkIntToScalar(x) + SK_ScalarHalf,
    179                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    180 
    181     SkFixed fx = SkScalarToFixed(srcPt.fX);
    182     SkFixed fy = SkScalarToFixed(srcPt.fY);
    183     SkFixed dx = s.fInvSx;
    184     SkFixed dy = s.fInvKy;
    185     int maxX = s.fBitmap->width() - 1;
    186     int maxY = s.fBitmap->height() - 1;
    187 
    188     /* NEON lets us do an 8x unrolling */
    189     if (count >= 8) {
    190         /* SkFixed is 16.16 fixed point */
    191         SkFixed dx4 = dx * 4;
    192         SkFixed dy4 = dy * 4;
    193         SkFixed dx8 = dx * 8;
    194         SkFixed dy8 = dy * 8;
    195 
    196         int32x4_t xbase, ybase;
    197         int32x4_t x2base, y2base;
    198         int16_t *dst16 = (int16_t *) xy;
    199 
    200         /* my sets of maxx/maxy for clamping */
    201         int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16);
    202         int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair));
    203 
    204         /* now build fx/fx+dx/fx+2dx/fx+3dx */
    205         /* avoid the 'xbase unitialized' warning...*/
    206         xbase = vdupq_n_s32(fx);
    207         xbase = vsetq_lane_s32(fx+dx, xbase, 1);
    208         xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
    209         xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);
    210 
    211         /* same for fy */
    212         /* avoid the 'ybase unitialized' warning...*/
    213         ybase = vdupq_n_s32(fy);
    214         ybase = vsetq_lane_s32(fy+dy, ybase, 1);
    215         ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
    216         ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);
    217 
    218         x2base = vaddq_s32(xbase, vdupq_n_s32(dx4));
    219         y2base = vaddq_s32(ybase, vdupq_n_s32(dy4));
    220 
    221         /* store & bump */
    222         do {
    223             int32x4_t xout, yout;
    224             int32x4_t x2out, y2out;
    225             int16x8_t hi16, hi16_2;
    226 
    227             xout = xbase;
    228             yout = ybase;
    229 
    230             /* overlay y's low16 with hi16 from x */
    231             /* so we properly shifted xyxyxyxy */
    232             yout = vsriq_n_s32(yout, xout, 16);
    233             hi16 = vreinterpretq_s16_s32 (yout);
    234 
    235             /* do the clamping; both guys get 0's */
    236             hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0));
    237             hi16 = vminq_s16 (hi16, maxXY);
    238 
    239             vst1q_s16 (dst16, hi16);
    240 
    241             /* and for the other 4 pieces of this iteration */
    242             x2out = x2base;
    243             y2out = y2base;
    244 
    245             /* overlay y's low16 with hi16 from x */
    246             /* so we properly shifted xyxyxyxy */
    247             y2out = vsriq_n_s32(y2out, x2out, 16);
    248             hi16_2 = vreinterpretq_s16_s32 (y2out);
    249 
    250             /* do the clamping; both guys get 0's */
    251             hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0));
    252             hi16_2 = vminq_s16 (hi16_2, maxXY);
    253 
    254             /* RBE: gcc regenerates dst16+8 all the time instead
    255              * of folding it into an addressing mode. *sigh* */
    256             vst1q_s16 (dst16+8, hi16_2);
    257 
    258             /* moving base and on to the next */
    259             xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8));
    260             ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8));
    261             x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8));
    262             y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8));
    263 
    264             dst16 += 16;		/* 8x32 aka 16x16 */
    265             count -= 8;
    266             fx += dx8;
    267             fy += dy8;
    268         } while (count >= 8);
    269         xy = (uint32_t *) dst16;
    270     }
    271 
    272     for (int i = count; i > 0; --i) {
    273         *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
    274         fx += dx; fy += dy;
    275     }
    276 }
    277 
    278 #undef	DEBUG_PERSP_NOFILTER
    279 
    280 static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
    281                                 uint32_t* SK_RESTRICT xy,
    282                                 int count, int x, int y) {
    283     SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
    284 
    285     PREAMBLE(s);
    286     /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */
    287     int maxX = s.fBitmap->width() - 1;
    288     int maxY = s.fBitmap->height() - 1;
    289 
    290     SkPerspIter   iter(*s.fInvMatrix,
    291                        SkIntToScalar(x) + SK_ScalarHalf,
    292                        SkIntToScalar(y) + SK_ScalarHalf, count);
    293 
    294     while ((count = iter.next()) != 0) {
    295         const SkFixed* SK_RESTRICT srcXY = iter.getXY();
    296 
    297 #if defined(DEBUG_PERSP_NOFILTER)
    298 	/* debugging stuff */
    299 	const SkFixed *end_srcXY = srcXY + (count*2);
    300 	uint32_t *end_xy = xy + (count);
    301 	const SkFixed *base_srcXY = srcXY;
    302 	uint32_t *base_xy = xy;
    303 	int base_count = count;
    304 #endif
    305 
    306 #if 1
    307         // 2009/9/30: crashes in ApiDemos - Views - Animation - 3D Transition
    308 	// 2009/10/9: reworked to avoid illegal (but allowed by gas) insn
    309 
    310         /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
    311          * but we immediately discard the low 16 bits...
    312          * so what we're going to do is vld4, which will give us
    313          * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
    314          * parts....
    315          */
    316         if (count >= 8) {
    317             int16_t *mysrc = (int16_t *) srcXY;
    318             int16_t *mydst = (int16_t *) xy;
    319             int16x4_t maxX4 = vdup_n_s16((int16_t)maxX);
    320             int16x4_t maxY4 = vdup_n_s16((int16_t)maxY);
    321             int16x4_t zero4 = vdup_n_s16(0);
    322 
    323 	    /* The constructs with local blocks for register assignments
    324 	     * and asm() instructions is to make keep any hard register
    325 	     * assignments to as small a scope as possible. and to avoid
    326 	     * burning call-preserved hard registers on the vld/vst
    327 	     * instructions.
    328 	     */
    329 
    330             do {
    331                 int16x4_t xlo, xhi, ylo, yhi;
    332                 int16x4_t x2lo, x2hi, y2lo, y2hi;
    333 
    334                 /* vld4 does the de-interleaving for us */
    335 		{
    336                     register int16x4_t t_xlo asm("d0");
    337                     register int16x4_t t_xhi asm("d1");
    338                     register int16x4_t t_ylo asm("d2");
    339                     register int16x4_t t_yhi asm("d3");
    340 
    341                     asm ("vld4.16	{d0-d3},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
    342                         : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
    343                         : "r" (mysrc)
    344                     );
    345 		    xlo = t_xlo;
    346 		    xhi = t_xhi;
    347 		    ylo = t_ylo;
    348 		    yhi = t_yhi;
    349 		}
    350 
    351                 /* clamp X>>16 (aka xhi) to 0..maxX */
    352                 xhi = vmax_s16(xhi, zero4);	/* now 0.. */
    353                 xhi = vmin_s16(xhi, maxX4);	/* now 0..maxX */
    354 
    355                 /* clamp Y>>16 (aka yhi) to 0..maxY */
    356                 yhi = vmax_s16(yhi, zero4);	/* now 0.. */
    357                 yhi = vmin_s16(yhi, maxY4);	/* now 0..maxY */
    358 
    359 		/* deal with the second set of numbers */
    360 		{
    361                     register int16x4_t t_xlo asm("d4");
    362                     register int16x4_t t_xhi asm("d5");
    363                     register int16x4_t t_ylo asm("d6");
    364                     register int16x4_t t_yhi asm("d7");
    365 
    366                     /* offset == 256 bits == 32 bytes == 8 longs == 16 shorts */
    367                     asm ("vld4.16	{d4-d7},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
    368                         : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
    369                         : "r" (mysrc+16)
    370                     );
    371 		    x2lo = t_xlo;
    372 		    x2hi = t_xhi;
    373 		    y2lo = t_ylo;
    374 		    y2hi = t_yhi;
    375 		}
    376 
    377                 /* clamp the second 4 here */
    378 
    379 		if (0) { extern void rbe(void); rbe(); }
    380 
    381                 /* clamp X>>16 (aka xhi) to 0..maxX */
    382                 x2hi = vmax_s16(x2hi, zero4);	/* now 0.. */
    383                 x2hi = vmin_s16(x2hi, maxX4);	/* now 0..maxX */
    384 
    385                 /* clamp Y>>16 (aka yhi) to 0..maxY */
    386                 y2hi = vmax_s16(y2hi, zero4);	/* now 0.. */
    387                 y2hi = vmin_s16(y2hi, maxY4);	/* now 0..maxY */
    388 
    389                 /* we're storing as {x,y}s: x is [0], y is [1] */
    390                 /* we'll use vst2 to make this happen */
    391 
    392 		{
    393                     register int16x4_t out_x asm("d16") = xhi;
    394                     register int16x4_t out_y asm("d17") = yhi;
    395 
    396                     asm ("vst2.16	{d16-d17},[%2]  /* xlo=%P0 xhi=%P1 */"
    397 			:
    398 			: "w" (out_x), "w" (out_y), "r" (mydst)
    399 			);
    400 		}
    401 		{
    402                     register int16x4_t out_x asm("d18") = x2hi;
    403                     register int16x4_t out_y asm("d19") = y2hi;
    404 
    405                     asm ("vst2.16	{d18-d19},[%2]  /* xlo=%P0 xhi=%P1 */"
    406 			:
    407 			: "w" (out_x), "w" (out_y), "r" (mydst+8)
    408 			);
    409 		}
    410 
    411                 /* XXX: gcc isn't interleaving these with the NEON ops
    412                  * but i think that all the scoreboarding works out */
    413                 count -= 8;	/* 8 iterations */
    414                 mysrc += 32;	/* 16 longs, aka 32 shorts */
    415                 mydst += 16;	/* 16 shorts, aka 8 longs */
    416             } while (count >= 8);
    417             /* get xy and srcXY fixed up */
    418             srcXY = (const SkFixed *) mysrc;
    419             xy = (uint32_t *) mydst;
    420         }
    421 #endif
    422 
    423         while (--count >= 0) {
    424             *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
    425                      TILEX_PROCF(srcXY[0], maxX);
    426             srcXY += 2;
    427         }
    428 
    429 #if defined(DEBUG_PERSP_NOFILTER)
    430 	/* for checking our NEON-produced results against vanilla code */
    431 	{
    432 	    int bad = (-1);
    433 	    for (int i = 0; i < base_count; i++) {
    434             uint32_t val;
    435             val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
    436                     TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
    437 
    438             if (val != base_xy[i]) {
    439                 bad = i;
    440                 break;
    441             }
    442 	    }
    443 	    if (bad >= 0) {
    444             SkDebugf("clamp-nofilter-persp failed piece %d\n", bad);
    445             SkDebugf("    maxX %08x maxY %08x\n", maxX, maxY);
    446             bad -= (bad & 0x7);	       /* align */
    447             for (int i = bad; i < bad + 8; i++) {
    448                 uint32_t val;
    449                 val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
    450                 TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
    451 
    452                 SkDebugf("%d: got %08x want %08x srcXY[0] %08x srcXY[1] %08x\n",
    453                           i, base_xy[i], val, base_srcXY[i * 2 + 0],
    454                  base_srcXY[i * 2 + 1]);
    455             }
    456             SkDebugf ("---\n");
    457 	    }
    458 
    459 	    if (end_xy != xy) {
    460             SkDebugf("xy ended at %08x, should be %08x\n", xy, end_xy);
    461 	    }
    462 	    if (end_srcXY != srcXY) {
    463             SkDebugf("srcXY ended at %08x, should be %08x\n", srcXY,
    464                       end_srcXY);
    465 	    }
    466 	}
    467 #endif
    468     }
    469 }
    470 
    471 #undef	DEBUG_PERSP_NOFILTER
    472 
    473 //////////////////////////////////////////////////////////////////////////////
    474 
    475 static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
    476                                           SkFixed one PREAMBLE_PARAM_Y) {
    477     unsigned i = TILEY_PROCF(f, max);
    478     i = (i << 4) | TILEY_LOW_BITS(f, max);
    479     return (i << 14) | (TILEY_PROCF((f + one), max));
    480 }
    481 
    482 static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
    483                                           SkFixed one PREAMBLE_PARAM_X) {
    484     unsigned i = TILEX_PROCF(f, max);
    485     i = (i << 4) | TILEX_LOW_BITS(f, max);
    486     return (i << 14) | (TILEX_PROCF((f + one), max));
    487 }
    488 
    489 static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
    490                               uint32_t xy[], int count, int x, int y) {
    491     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
    492                              SkMatrix::kScale_Mask)) == 0);
    493     SkASSERT(s.fInvKy == 0);
    494 
    495     PREAMBLE(s);
    496 
    497     const unsigned maxX = s.fBitmap->width() - 1;
    498     const SkFixed one = s.fFilterOneX;
    499     const SkFixed dx = s.fInvSx;
    500     SkFixed fx;
    501 
    502     {
    503         SkPoint pt;
    504         s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
    505                                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
    506         const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
    507         const unsigned maxY = s.fBitmap->height() - 1;
    508         // compute our two Y values up front
    509         *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
    510         // now initialize fx
    511         fx = SkScalarToFixed(pt.fX) - (one >> 1);
    512     }
    513 
    514 #ifdef CHECK_FOR_DECAL
    515     // test if we don't need to apply the tile proc
    516     if (dx > 0 &&
    517             (unsigned)(fx >> 16) <= maxX &&
    518             (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
    519         decal_filter_scale(xy, fx, dx, count);
    520     } else
    521 #endif
    522 
    523     if (count >= 4) {
    524         int32x4_t wide_dx, wide_one;
    525         int32x4_t wide_fx, wide_fx1, wide_i, wide_lo;
    526     #if 0
    527         /* verification hooks -- see below */
    528         SkFixed debug_fx = fx;
    529         int count_done = 0;
    530     #endif
    531 
    532         wide_fx = vdupq_n_s32(fx);
    533         wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
    534         wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
    535         wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
    536 
    537         wide_dx = vdupq_n_s32(dx);
    538         wide_one = vdupq_n_s32(one);
    539 
    540         while (count >= 4) {
    541             /* original expands to:
    542              * unsigned i = SkClampMax((f) >> 16, max);
    543              * i = (i << 4) | (((f) >> 12) & 0xF);
    544              * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
    545              */
    546 
    547             /* i = SkClampMax(f>>16, maxX) */
    548             wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
    549             wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
    550 
    551             /* i<<4 | TILEX_LOW_BITS(fx) */
    552             wide_lo = vshrq_n_s32(wide_fx, 12);
    553             wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
    554 
    555             /* i<<14 */
    556             wide_i = vshlq_n_s32(wide_i, 14);
    557 
    558             /* SkClampMax(((f + one)) >> 16, max) */
    559             wide_fx1 = vaddq_s32(wide_fx, wide_one);
    560             wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
    561             wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
    562 
    563             /* final combination */
    564             wide_i = vorrq_s32(wide_i, wide_fx1);
    565 
    566             vst1q_u32(xy, vreinterpretq_u32_s32(wide_i));
    567 
    568     #if 0
    569             /* having a verification hook is a good idea */
    570             /* use debug_fx, debug_fx+dx, etc. */
    571 
    572             for (int i=0;i<4;i++) {
    573             uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X);
    574                     if (xy[i] != want)
    575                 {
    576                 /* print a nastygram */
    577                 SkDebugf("clamp-filter-scale fails\n");
    578                 SkDebugf("got %08x want %08x\n", xy[i], want);
    579                 SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n",
    580                 fx, debug_fx, dx, count_done);
    581                 SkDebugf(" maxX %08x one %08x\n", maxX, one);
    582 
    583                 }
    584             debug_fx += dx;
    585             count_done++;
    586             }
    587     #endif
    588             wide_fx += vdupq_n_s32(dx+dx+dx+dx);
    589             fx += dx+dx+dx+dx;
    590             xy += 4;
    591             count -= 4;
    592         }
    593     }
    594 
    595     while (--count >= 0) {
    596         *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
    597         fx += dx;
    598     }
    599 }
    600 
    601 static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
    602                                uint32_t xy[], int count, int x, int y) {
    603     SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
    604     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
    605                              SkMatrix::kScale_Mask |
    606                              SkMatrix::kAffine_Mask)) == 0);
    607 
    608     PREAMBLE(s);
    609     SkPoint srcPt;
    610     s.fInvProc(*s.fInvMatrix,
    611                SkIntToScalar(x) + SK_ScalarHalf,
    612                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
    613 
    614     SkFixed oneX = s.fFilterOneX;
    615     SkFixed oneY = s.fFilterOneY;
    616     SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
    617     SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
    618     SkFixed dx = s.fInvSx;
    619     SkFixed dy = s.fInvKy;
    620     unsigned maxX = s.fBitmap->width() - 1;
    621     unsigned maxY = s.fBitmap->height() - 1;
    622 
    623     if (count >= 4) {
    624         int32x4_t wide_one, wide_i, wide_lo;
    625         int32x4_t wide_dx, wide_fx, wide_onex, wide_fx1;
    626         int32x4_t wide_dy, wide_fy, wide_oney, wide_fy1;
    627 
    628     #undef	AFFINE_DEBUG
    629     #if	defined(AFFINE_DEBUG)
    630         SkFixed fyp = fy;
    631         SkFixed fxp = fx;
    632         uint32_t *xyp = xy;
    633         int count_done = 0;
    634     #endif
    635 
    636         wide_fx = vdupq_n_s32(fx);
    637         wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
    638         wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
    639         wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
    640         wide_dx = vdupq_n_s32(dx);
    641 
    642         wide_fy = vdupq_n_s32(fy);
    643         wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1);
    644         wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2);
    645         wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3);
    646         wide_dy = vdupq_n_s32(dy);
    647 
    648         wide_onex = vdupq_n_s32(oneX);
    649         wide_oney = vdupq_n_s32(oneY);
    650 
    651         while (count >= 4) {
    652             int32x4_t wide_x;
    653             int32x4_t wide_y;
    654 
    655             /* do the X side, then the Y side, then interleave them */
    656 
    657             /* original expands to:
    658              * unsigned i = SkClampMax((f) >> 16, max);
    659              * i = (i << 4) | (((f) >> 12) & 0xF);
    660              * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
    661              */
    662 
    663             /* i = SkClampMax(f>>16, maxX) */
    664             wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
    665             wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
    666 
    667             /* i<<4 | TILEX_LOW_BITS(fx) */
    668             wide_lo = vshrq_n_s32(wide_fx, 12);
    669             wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
    670 
    671             /* i<<14 */
    672             wide_i = vshlq_n_s32(wide_i, 14);
    673 
    674             /* SkClampMax(((f + one)) >> 16, max) */
    675             wide_fx1 = vaddq_s32(wide_fx, wide_onex);
    676             wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
    677             wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
    678 
    679             /* final combination */
    680             wide_x = vorrq_s32(wide_i, wide_fx1);
    681 
    682             /* And now the Y side */
    683 
    684             /* i = SkClampMax(f>>16, maxX) */
    685             wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0));
    686             wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY));
    687 
    688             /* i<<4 | TILEX_LOW_BITS(fx) */
    689             wide_lo = vshrq_n_s32(wide_fy, 12);
    690             wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
    691 
    692             /* i<<14 */
    693             wide_i = vshlq_n_s32(wide_i, 14);
    694 
    695             /* SkClampMax(((f + one)) >> 16, max) */
    696             wide_fy1 = vaddq_s32(wide_fy, wide_oney);
    697             wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0));
    698             wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY));
    699 
    700             /* final combination */
    701             wide_y = vorrq_s32(wide_i, wide_fy1);
    702 
    703             /* interleave as YXYXYXYX as part of the storing */
    704 	    {
    705                 /* vst2.32 needs side-by-side registers */
    706                 register int32x4_t t_x asm("q1");
    707                 register int32x4_t t_y asm("q0");
    708 
    709 		t_x = wide_x; t_y = wide_y;
    710                 asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
    711                     :
    712                     : "w" (t_y), "w" (t_x), "r" (xy)
    713                     );
    714 	    }
    715 
    716     #if	defined(AFFINE_DEBUG)
    717             /* make sure we're good here -- check the 4 we just output */
    718             for (int i = 0; i<4;i++) {
    719             uint32_t val;
    720             val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y);
    721             if (val != xy[i*2+0]) {
    722                 /* print a nastygram */
    723                 SkDebugf("clamp-filter-affine fails\n");
    724                 SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val);
    725                 SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
    726                 fy, fxp, fyp, dx, dy, count_done);
    727                 SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY);
    728                 }
    729             val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X);
    730             if (val != xy[i*2+1]) {
    731                 /* print a nastygram */
    732                 SkDebugf("clamp-filter-affine fails\n");
    733                 SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val);
    734                 SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
    735                 fx, fxp, fyp, dx, dy, count_done);
    736                 SkDebugf(" maxX %08x one %08x\n", maxX, oneX);
    737             }
    738             fyp += dy;
    739             fxp += dx;
    740             count_done++;
    741             }
    742     #endif
    743 
    744             wide_fx += vdupq_n_s32(dx+dx+dx+dx);
    745             fx += dx+dx+dx+dx;
    746             wide_fy += vdupq_n_s32(dy+dy+dy+dy);
    747             fy += dy+dy+dy+dy;
    748             xy += 8;		/* 4 x's, 4 y's */
    749             count -= 4;
    750         }
    751     }
    752 
    753     while (--count >= 0) {
    754         /* NB: writing Y/X */
    755         *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
    756         fy += dy;
    757         *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
    758         fx += dx;
    759     }
    760 }
    761 
    762 static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
    763                               uint32_t* SK_RESTRICT xy, int count,
    764                               int x, int y) {
    765     SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
    766 
    767     PREAMBLE(s);
    768     unsigned maxX = s.fBitmap->width() - 1;
    769     unsigned maxY = s.fBitmap->height() - 1;
    770     SkFixed oneX = s.fFilterOneX;
    771     SkFixed oneY = s.fFilterOneY;
    772 
    773     SkPerspIter   iter(*s.fInvMatrix,
    774                        SkIntToScalar(x) + SK_ScalarHalf,
    775                        SkIntToScalar(y) + SK_ScalarHalf, count);
    776 
    777     while ((count = iter.next()) != 0) {
    778         const SkFixed* SK_RESTRICT srcXY = iter.getXY();
    779 
    780         if (count >= 4) {
    781             int32x4_t wide_one, wide_i, wide_lo;
    782             int32x4_t wide_fx1;
    783             int32x4_t wide_fy1;
    784             int32x4_t wide_x, wide_y;
    785 
    786             while (count >= 4) {
    787                 /* RBE: it's good, but:
    788                  * -- we spill a constant that could be easily regnerated
    789                  *    [perhaps tweak gcc's NEON constant costs?]
    790                  */
    791 
    792                 /* load src:  x-y-x-y-x-y-x-y */
    793 		{
    794 		    register int32x4_t q0 asm ("q0");
    795 		    register int32x4_t q1 asm ("q1");
    796                     asm ("vld2.32	{q0-q1},[%2]  /* x=%q0 y=%q1 */"
    797                          : "=w" (q0), "=w" (q1)
    798                          : "r" (srcXY));
    799 		    wide_x = q0; wide_y = q1;
    800 		}
    801 
    802                 /* do the X side, then the Y side, then interleave them */
    803 
    804                 wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1));
    805 
    806                 /* original expands to:
    807                  * unsigned i = SkClampMax((f) >> 16, max);
    808                  * i = (i << 4) | (((f) >> 12) & 0xF);
    809                  * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
    810                  */
    811 
    812                 /* i = SkClampMax(f>>16, maxX) */
    813                 wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0));
    814                 wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX));
    815 
    816                 /* i<<4 | TILEX_LOW_BITS(fx) */
    817                 wide_lo = vshrq_n_s32 (wide_x, 12);
    818                 wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
    819 
    820                 /* i<<14 */
    821                 wide_i = vshlq_n_s32 (wide_i, 14);
    822 
    823                 /* SkClampMax(((f + one)) >> 16, max) */
    824                 wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX));
    825                 wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0));
    826                 wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX));
    827 
    828                 /* final combination */
    829                 wide_x = vorrq_s32 (wide_i, wide_fx1);
    830 
    831 
    832                 /* And now the Y side */
    833 
    834                 wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1));
    835 
    836                 /* i = SkClampMax(f>>16, maxX) */
    837                 wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0));
    838                 wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY));
    839 
    840                 /* i<<4 | TILEX_LOW_BITS(fx) */
    841                 wide_lo = vshrq_n_s32 (wide_y, 12);
    842                 wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
    843 
    844                 /* i<<14 */
    845                 wide_i = vshlq_n_s32 (wide_i, 14);
    846 
    847                 /* SkClampMax(((f + one)) >> 16, max) */
    848 
    849                 /* wide_fy1_1 and wide_fy1_2 are just temporary variables to
    850                  * work-around an ICE in debug */
    851                 int32x4_t wide_fy1_1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY));
    852                 int32x4_t wide_fy1_2 = vmaxq_s32 (vshrq_n_s32 (wide_fy1_1, 16),
    853                                                   vdupq_n_s32 (0));
    854                 wide_fy1 = vminq_s32 (wide_fy1_2, vdupq_n_s32 (maxY));
    855 
    856                 /* final combination */
    857                 wide_y = vorrq_s32 (wide_i, wide_fy1);
    858 
    859                 /* switch them around; have to do it this way to get them
    860                  * in the proper registers to match our instruction */
    861 
    862                 /* iteration bookkeeping, ahead of the asm() for scheduling */
    863                 srcXY += 2*4;
    864                 count -= 4;
    865 
    866                 /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */
    867 		{
    868 		    register int32x4_t q0 asm ("q0") = wide_y;
    869 		    register int32x4_t q1 asm ("q1") = wide_x;
    870 
    871                     asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
    872                         :
    873                         : "w" (q0), "w" (q1), "r" (xy));
    874 		}
    875 
    876                 /* on to the next iteration */
    877                 /* count, srcXY are handled above */
    878                 xy += 2*4;
    879             }
    880         }
    881 
    882         /* was do-while; NEON code invalidates original count>0 assumption */
    883         while (--count >= 0) {
    884 	    /* NB: we read x/y, we write y/x */
    885             *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
    886                                        oneY PREAMBLE_ARG_Y);
    887             *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
    888                                        oneX PREAMBLE_ARG_X);
    889             srcXY += 2;
    890         }
    891     }
    892 }
    893 
    894 static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
    895     SCALE_NOFILTER_NAME,
    896     SCALE_FILTER_NAME,
    897     AFFINE_NOFILTER_NAME,
    898     AFFINE_FILTER_NAME,
    899     PERSP_NOFILTER_NAME,
    900     PERSP_FILTER_NAME
    901 };
    902 
    903 #undef MAKENAME
    904 #undef TILEX_PROCF
    905 #undef TILEY_PROCF
    906 #ifdef CHECK_FOR_DECAL
    907     #undef CHECK_FOR_DECAL
    908 #endif
    909 
    910 #undef SCALE_NOFILTER_NAME
    911 #undef SCALE_FILTER_NAME
    912 #undef AFFINE_NOFILTER_NAME
    913 #undef AFFINE_FILTER_NAME
    914 #undef PERSP_NOFILTER_NAME
    915 #undef PERSP_FILTER_NAME
    916 
    917 #undef PREAMBLE
    918 #undef PREAMBLE_PARAM_X
    919 #undef PREAMBLE_PARAM_Y
    920 #undef PREAMBLE_ARG_X
    921 #undef PREAMBLE_ARG_Y
    922 
    923 #undef TILEX_LOW_BITS
    924 #undef TILEY_LOW_BITS
    925