Home | History | Annotate | Download | only in core
      1 
      2 /*
      3  * Copyright 2011 The Android Open Source Project
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      7  */
      8 
      9 
     10 #include "SkScan.h"
     11 #include "SkBlitter.h"
     12 #include "SkColorPriv.h"
     13 #include "SkLineClipper.h"
     14 #include "SkRasterClip.h"
     15 #include "SkFDot6.h"
     16 
     17 /*  Our attempt to compute the worst case "bounds" for the horizontal and
     18     vertical cases has some numerical bug in it, and we sometimes undervalue
     19     our extends. The bug is that when this happens, we will set the clip to
     20     NULL (for speed), and thus draw outside of the clip by a pixel, which might
     21     only look bad, but it might also access memory outside of the valid range
     22     allcoated for the device bitmap.
     23 
     24     This define enables our fix to outset our "bounds" by 1, thus avoiding the
     25     chance of the bug, but at the cost of sometimes taking the rectblitter
     26     case (i.e. not setting the clip to NULL) when we might not actually need
     27     to. If we can improve/fix the actual calculations, then we can remove this
     28     step.
     29  */
     30 #define OUTSET_BEFORE_CLIP_TEST     true
     31 
     32 #define HLINE_STACK_BUFFER      100
     33 
     34 static inline int SmallDot6Scale(int value, int dot6) {
     35     SkASSERT((int16_t)value == value);
     36     SkASSERT((unsigned)dot6 <= 64);
     37     return SkMulS16(value, dot6) >> 6;
     38 }
     39 
     40 //#define TEST_GAMMA
     41 
     42 #ifdef TEST_GAMMA
     43     static uint8_t gGammaTable[256];
     44     #define ApplyGamma(table, alpha)    (table)[alpha]
     45 
     46     static void build_gamma_table() {
     47         static bool gInit = false;
     48 
     49         if (gInit == false) {
     50             for (int i = 0; i < 256; i++) {
     51                 SkFixed n = i * 257;
     52                 n += n >> 15;
     53                 SkASSERT(n >= 0 && n <= SK_Fixed1);
     54                 n = SkFixedSqrt(n);
     55                 n = n * 255 >> 16;
     56             //  SkDebugf("morph %d -> %d\n", i, n);
     57                 gGammaTable[i] = SkToU8(n);
     58             }
     59             gInit = true;
     60         }
     61     }
     62 #else
     63     #define ApplyGamma(table, alpha)    SkToU8(alpha)
     64 #endif
     65 
     66 ///////////////////////////////////////////////////////////////////////////////
     67 
     68 static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
     69                                U8CPU alpha) {
     70     SkASSERT(count > 0);
     71 
     72     int16_t runs[HLINE_STACK_BUFFER + 1];
     73     uint8_t  aa[HLINE_STACK_BUFFER];
     74 
     75     aa[0] = ApplyGamma(gGammaTable, alpha);
     76     do {
     77         int n = count;
     78         if (n > HLINE_STACK_BUFFER) {
     79             n = HLINE_STACK_BUFFER;
     80         }
     81         runs[0] = SkToS16(n);
     82         runs[n] = 0;
     83         blitter->blitAntiH(x, y, aa, runs);
     84         x += n;
     85         count -= n;
     86     } while (count > 0);
     87 }
     88 
     89 static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/,
     90                      SkBlitter* blitter, int mod64) {
     91     SkASSERT(x < stopx);
     92     int count = stopx - x;
     93     fy += SK_Fixed1/2;
     94 
     95     int y = fy >> 16;
     96     uint8_t  a = (uint8_t)(fy >> 8);
     97 
     98     // lower line
     99     unsigned ma = SmallDot6Scale(a, mod64);
    100     if (ma) {
    101         call_hline_blitter(blitter, x, y, count, ma);
    102     }
    103 
    104     // upper line
    105     ma = SmallDot6Scale(255 - a, mod64);
    106     if (ma) {
    107         call_hline_blitter(blitter, x, y - 1, count, ma);
    108     }
    109 
    110     return fy - SK_Fixed1/2;
    111 }
    112 
    113 static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy,
    114                       SkBlitter* blitter, int mod64) {
    115     SkASSERT(x < stopx);
    116 
    117 #ifdef TEST_GAMMA
    118     const uint8_t* gamma = gGammaTable;
    119 #endif
    120     int16_t runs[2];
    121     uint8_t  aa[1];
    122 
    123     runs[0] = 1;
    124     runs[1] = 0;
    125 
    126     fy += SK_Fixed1/2;
    127     do {
    128         int lower_y = fy >> 16;
    129         uint8_t  a = (uint8_t)(fy >> 8);
    130         unsigned ma = SmallDot6Scale(a, mod64);
    131         if (ma) {
    132             aa[0] = ApplyGamma(gamma, ma);
    133             blitter->blitAntiH(x, lower_y, aa, runs);
    134             // the clipping blitters might edit runs, but should not affect us
    135             SkASSERT(runs[0] == 1);
    136             SkASSERT(runs[1] == 0);
    137         }
    138         ma = SmallDot6Scale(255 - a, mod64);
    139         if (ma) {
    140             aa[0] = ApplyGamma(gamma, ma);
    141             blitter->blitAntiH(x, lower_y - 1, aa, runs);
    142             // the clipping blitters might edit runs, but should not affect us
    143             SkASSERT(runs[0] == 1);
    144             SkASSERT(runs[1] == 0);
    145         }
    146         fy += dy;
    147     } while (++x < stopx);
    148 
    149     return fy - SK_Fixed1/2;
    150 }
    151 
    152 static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/,
    153                      SkBlitter* blitter, int mod64) {
    154     SkASSERT(y < stopy);
    155     fx += SK_Fixed1/2;
    156 
    157     int x = fx >> 16;
    158     int a = (uint8_t)(fx >> 8);
    159 
    160     unsigned ma = SmallDot6Scale(a, mod64);
    161     if (ma) {
    162         blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
    163     }
    164     ma = SmallDot6Scale(255 - a, mod64);
    165     if (ma) {
    166         blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
    167     }
    168 
    169     return fx - SK_Fixed1/2;
    170 }
    171 
    172 static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx,
    173                        SkBlitter* blitter, int mod64) {
    174     SkASSERT(y < stopy);
    175 #ifdef TEST_GAMMA
    176     const uint8_t* gamma = gGammaTable;
    177 #endif
    178     int16_t runs[3];
    179     uint8_t  aa[2];
    180 
    181     runs[0] = 1;
    182     runs[2] = 0;
    183 
    184     fx += SK_Fixed1/2;
    185     do {
    186         int x = fx >> 16;
    187         uint8_t  a = (uint8_t)(fx >> 8);
    188 
    189         aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
    190         aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
    191         // the clippng blitters might overwrite this guy, so we have to reset it each time
    192         runs[1] = 1;
    193         blitter->blitAntiH(x - 1, y, aa, runs);
    194         // the clipping blitters might edit runs, but should not affect us
    195         SkASSERT(runs[0] == 1);
    196         SkASSERT(runs[2] == 0);
    197         fx += dx;
    198     } while (++y < stopy);
    199 
    200     return fx - SK_Fixed1/2;
    201 }
    202 
    203 typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
    204                             SkFixed slope, SkBlitter*, int);
    205 
    206 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
    207     SkASSERT((a << 16 >> 16) == a);
    208     SkASSERT(b != 0);
    209     return (a << 16) / b;
    210 }
    211 
    212 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
    213                              const SkIRect* clip, SkBlitter* blitter) {
    214     // check that we're no larger than 511 pixels (so we can do a faster div).
    215     // if we are, subdivide and call again
    216 
    217     if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
    218         /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
    219             precise, but avoids overflowing the intermediate result if the
    220             values are huge. A better fix might be to clip the original pts
    221             directly (i.e. do the divide), so we don't spend time subdividing
    222             huge lines at all.
    223          */
    224         int hx = (x0 >> 1) + (x1 >> 1);
    225         int hy = (y0 >> 1) + (y1 >> 1);
    226         do_anti_hairline(x0, y0, hx, hy, clip, blitter);
    227         do_anti_hairline(hx, hy, x1, y1, clip, blitter);
    228         return;
    229     }
    230 
    231     int         scaleStart, scaleStop;
    232     int         istart, istop;
    233     SkFixed     fstart, slope;
    234     LineProc    proc;
    235 
    236     if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
    237         if (x0 > x1) {    // we want to go left-to-right
    238             SkTSwap<SkFDot6>(x0, x1);
    239             SkTSwap<SkFDot6>(y0, y1);
    240         }
    241 
    242         istart = SkFDot6Floor(x0);
    243         istop = SkFDot6Ceil(x1);
    244         fstart = SkFDot6ToFixed(y0);
    245         if (y0 == y1) {   // completely horizontal, take fast case
    246             slope = 0;
    247             proc = hline;
    248         } else {
    249             slope = fastfixdiv(y1 - y0, x1 - x0);
    250             SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
    251             fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
    252             proc = horish;
    253         }
    254 
    255         SkASSERT(istop > istart);
    256         if (istop - istart == 1) {
    257             scaleStart = x1 - x0;
    258             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
    259             scaleStop = 0;
    260         } else {
    261             scaleStart = 64 - (x0 & 63);
    262             scaleStop = x1 & 63;
    263         }
    264 
    265         if (clip){
    266             if (istart >= clip->fRight || istop <= clip->fLeft) {
    267                 return;
    268             }
    269             if (istart < clip->fLeft) {
    270                 fstart += slope * (clip->fLeft - istart);
    271                 istart = clip->fLeft;
    272                 scaleStart = 64;
    273             }
    274             if (istop > clip->fRight) {
    275                 istop = clip->fRight;
    276                 scaleStop = 64;
    277             }
    278             SkASSERT(istart <= istop);
    279             if (istart == istop) {
    280                 return;
    281             }
    282             // now test if our Y values are completely inside the clip
    283             int top, bottom;
    284             if (slope >= 0) { // T2B
    285                 top = SkFixedFloor(fstart - SK_FixedHalf);
    286                 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
    287             } else {           // B2T
    288                 bottom = SkFixedCeil(fstart + SK_FixedHalf);
    289                 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
    290             }
    291 #ifdef OUTSET_BEFORE_CLIP_TEST
    292             top -= 1;
    293             bottom += 1;
    294 #endif
    295             if (top >= clip->fBottom || bottom <= clip->fTop) {
    296                 return;
    297             }
    298             if (clip->fTop <= top && clip->fBottom >= bottom) {
    299                 clip = NULL;
    300             }
    301         }
    302     } else {   // mostly vertical
    303         if (y0 > y1) {  // we want to go top-to-bottom
    304             SkTSwap<SkFDot6>(x0, x1);
    305             SkTSwap<SkFDot6>(y0, y1);
    306         }
    307 
    308         istart = SkFDot6Floor(y0);
    309         istop = SkFDot6Ceil(y1);
    310         fstart = SkFDot6ToFixed(x0);
    311         if (x0 == x1) {
    312             if (y0 == y1) { // are we zero length?
    313                 return;     // nothing to do
    314             }
    315             slope = 0;
    316             proc = vline;
    317         } else {
    318             slope = fastfixdiv(x1 - x0, y1 - y0);
    319             SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
    320             fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
    321             proc = vertish;
    322         }
    323 
    324         SkASSERT(istop > istart);
    325         if (istop - istart == 1) {
    326             scaleStart = y1 - y0;
    327             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
    328             scaleStop = 0;
    329         } else {
    330             scaleStart = 64 - (y0 & 63);
    331             scaleStop = y1 & 63;
    332         }
    333 
    334         if (clip) {
    335             if (istart >= clip->fBottom || istop <= clip->fTop) {
    336                 return;
    337             }
    338             if (istart < clip->fTop) {
    339                 fstart += slope * (clip->fTop - istart);
    340                 istart = clip->fTop;
    341                 scaleStart = 64;
    342             }
    343             if (istop > clip->fBottom) {
    344                 istop = clip->fBottom;
    345                 scaleStop = 64;
    346             }
    347             SkASSERT(istart <= istop);
    348             if (istart == istop)
    349                 return;
    350 
    351             // now test if our X values are completely inside the clip
    352             int left, right;
    353             if (slope >= 0) { // L2R
    354                 left = SkFixedFloor(fstart - SK_FixedHalf);
    355                 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
    356             } else {           // R2L
    357                 right = SkFixedCeil(fstart + SK_FixedHalf);
    358                 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
    359             }
    360 #ifdef OUTSET_BEFORE_CLIP_TEST
    361             left -= 1;
    362             right += 1;
    363 #endif
    364             if (left >= clip->fRight || right <= clip->fLeft) {
    365                 return;
    366             }
    367             if (clip->fLeft <= left && clip->fRight >= right) {
    368                 clip = NULL;
    369             }
    370         }
    371     }
    372 
    373     SkRectClipBlitter   rectClipper;
    374     if (clip) {
    375         rectClipper.init(blitter, *clip);
    376         blitter = &rectClipper;
    377     }
    378 
    379     fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
    380     istart += 1;
    381     int fullSpans = istop - istart - (scaleStop > 0);
    382     if (fullSpans > 0) {
    383         fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
    384     }
    385     if (scaleStop > 0) {
    386         proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
    387     }
    388 }
    389 
    390 void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
    391                              const SkRegion* clip, SkBlitter* blitter) {
    392     if (clip && clip->isEmpty()) {
    393         return;
    394     }
    395 
    396     SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
    397 
    398 #ifdef TEST_GAMMA
    399     build_gamma_table();
    400 #endif
    401 
    402     SkPoint pts[2] = { pt0, pt1 };
    403 
    404     if (clip) {
    405         SkRect clipBounds;
    406         clipBounds.set(clip->getBounds());
    407         /*  We perform integral clipping later on, but we do a scalar clip first
    408             to ensure that our coordinates are expressible in fixed/integers.
    409 
    410             antialiased hairlines can draw up to 1/2 of a pixel outside of
    411             their bounds, so we need to outset the clip before calling the
    412             clipper. To make the numerics safer, we outset by a whole pixel,
    413             since the 1/2 pixel boundary is important to the antihair blitter,
    414             we don't want to risk numerical fate by chopping on that edge.
    415          */
    416         clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
    417 
    418         if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
    419             return;
    420         }
    421     }
    422 
    423     SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
    424     SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
    425     SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
    426     SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
    427 
    428     if (clip) {
    429         SkFDot6 left = SkMin32(x0, x1);
    430         SkFDot6 top = SkMin32(y0, y1);
    431         SkFDot6 right = SkMax32(x0, x1);
    432         SkFDot6 bottom = SkMax32(y0, y1);
    433         SkIRect ir;
    434 
    435         ir.set( SkFDot6Floor(left) - 1,
    436                 SkFDot6Floor(top) - 1,
    437                 SkFDot6Ceil(right) + 1,
    438                 SkFDot6Ceil(bottom) + 1);
    439 
    440         if (clip->quickReject(ir)) {
    441             return;
    442         }
    443         if (!clip->quickContains(ir)) {
    444             SkRegion::Cliperator iter(*clip, ir);
    445             const SkIRect*       r = &iter.rect();
    446 
    447             while (!iter.done()) {
    448                 do_anti_hairline(x0, y0, x1, y1, r, blitter);
    449                 iter.next();
    450             }
    451             return;
    452         }
    453         // fall through to no-clip case
    454     }
    455     do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
    456 }
    457 
    458 void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
    459                           SkBlitter* blitter) {
    460     SkPoint p0, p1;
    461 
    462     p0.set(rect.fLeft, rect.fTop);
    463     p1.set(rect.fRight, rect.fTop);
    464     SkScan::AntiHairLine(p0, p1, clip, blitter);
    465     p0.set(rect.fRight, rect.fBottom);
    466     SkScan::AntiHairLine(p0, p1, clip, blitter);
    467     p1.set(rect.fLeft, rect.fBottom);
    468     SkScan::AntiHairLine(p0, p1, clip, blitter);
    469     p0.set(rect.fLeft, rect.fTop);
    470     SkScan::AntiHairLine(p0, p1, clip, blitter);
    471 }
    472 
    473 ///////////////////////////////////////////////////////////////////////////////
    474 
    475 typedef int FDot8;  // 24.8 integer fixed point
    476 
    477 static inline FDot8 SkFixedToFDot8(SkFixed x) {
    478     return (x + 0x80) >> 8;
    479 }
    480 
    481 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
    482                         SkBlitter* blitter) {
    483     SkASSERT(L < R);
    484 
    485     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
    486         blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
    487         return;
    488     }
    489 
    490     int left = L >> 8;
    491 
    492     if (L & 0xFF) {
    493         blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
    494         left += 1;
    495     }
    496 
    497     int rite = R >> 8;
    498     int width = rite - left;
    499     if (width > 0) {
    500         call_hline_blitter(blitter, left, top, width, alpha);
    501     }
    502     if (R & 0xFF) {
    503         blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
    504     }
    505 }
    506 
    507 static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
    508                          bool fillInner) {
    509     // check for empty now that we're in our reduced precision space
    510     if (L >= R || T >= B) {
    511         return;
    512     }
    513     int top = T >> 8;
    514     if (top == ((B - 1) >> 8)) {   // just one scanline high
    515         do_scanline(L, top, R, B - T - 1, blitter);
    516         return;
    517     }
    518 
    519     if (T & 0xFF) {
    520         do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
    521         top += 1;
    522     }
    523 
    524     int bot = B >> 8;
    525     int height = bot - top;
    526     if (height > 0) {
    527         int left = L >> 8;
    528         if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
    529             blitter->blitV(left, top, height, R - L - 1);
    530         } else {
    531             if (L & 0xFF) {
    532                 blitter->blitV(left, top, height, 256 - (L & 0xFF));
    533                 left += 1;
    534             }
    535             int rite = R >> 8;
    536             int width = rite - left;
    537             if (width > 0 && fillInner) {
    538                 blitter->blitRect(left, top, width, height);
    539             }
    540             if (R & 0xFF) {
    541                 blitter->blitV(rite, top, height, R & 0xFF);
    542             }
    543         }
    544     }
    545 
    546     if (B & 0xFF) {
    547         do_scanline(L, bot, R, B & 0xFF, blitter);
    548     }
    549 }
    550 
    551 static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
    552     antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
    553                  SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
    554                  blitter, true);
    555 }
    556 
    557 ///////////////////////////////////////////////////////////////////////////////
    558 
    559 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
    560                           SkBlitter* blitter) {
    561     if (NULL == clip) {
    562         antifillrect(xr, blitter);
    563     } else {
    564         SkIRect outerBounds;
    565         XRect_roundOut(xr, &outerBounds);
    566 
    567         if (clip->isRect()) {
    568             const SkIRect& clipBounds = clip->getBounds();
    569 
    570             if (clipBounds.contains(outerBounds)) {
    571                 antifillrect(xr, blitter);
    572             } else {
    573                 SkXRect tmpR;
    574                 // this keeps our original edges fractional
    575                 XRect_set(&tmpR, clipBounds);
    576                 if (tmpR.intersect(xr)) {
    577                     antifillrect(tmpR, blitter);
    578                 }
    579             }
    580         } else {
    581             SkRegion::Cliperator clipper(*clip, outerBounds);
    582             const SkIRect&       rr = clipper.rect();
    583 
    584             while (!clipper.done()) {
    585                 SkXRect  tmpR;
    586 
    587                 // this keeps our original edges fractional
    588                 XRect_set(&tmpR, rr);
    589                 if (tmpR.intersect(xr)) {
    590                     antifillrect(tmpR, blitter);
    591                 }
    592                 clipper.next();
    593             }
    594         }
    595     }
    596 }
    597 
    598 void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
    599                            SkBlitter* blitter) {
    600     if (clip.isBW()) {
    601         AntiFillXRect(xr, &clip.bwRgn(), blitter);
    602     } else {
    603         SkIRect outerBounds;
    604         XRect_roundOut(xr, &outerBounds);
    605 
    606         if (clip.quickContains(outerBounds)) {
    607             AntiFillXRect(xr, NULL, blitter);
    608         } else {
    609             SkAAClipBlitterWrapper wrapper(clip, blitter);
    610             blitter = wrapper.getBlitter();
    611 
    612             AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
    613         }
    614     }
    615 }
    616 
    617 #ifdef SK_SCALAR_IS_FLOAT
    618 
    619 /*  This guy takes a float-rect, but with the key improvement that it has
    620     already been clipped, so we know that it is safe to convert it into a
    621     XRect (fixedpoint), as it won't overflow.
    622 */
    623 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
    624     SkXRect xr;
    625 
    626     XRect_set(&xr, r);
    627     antifillrect(xr, blitter);
    628 }
    629 
    630 /*  We repeat the clipping logic of AntiFillXRect because the float rect might
    631     overflow if we blindly converted it to an XRect. This sucks that we have to
    632     repeat the clipping logic, but I don't see how to share the code/logic.
    633 
    634     We clip r (as needed) into one or more (smaller) float rects, and then pass
    635     those to our version of antifillrect, which converts it into an XRect and
    636     then calls the blit.
    637 */
    638 void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
    639                           SkBlitter* blitter) {
    640     if (clip) {
    641         SkRect newR;
    642         newR.set(clip->getBounds());
    643         if (!newR.intersect(origR)) {
    644             return;
    645         }
    646 
    647         SkIRect outerBounds;
    648         newR.roundOut(&outerBounds);
    649 
    650         if (clip->isRect()) {
    651             antifillrect(newR, blitter);
    652         } else {
    653             SkRegion::Cliperator clipper(*clip, outerBounds);
    654             while (!clipper.done()) {
    655                 newR.set(clipper.rect());
    656                 if (newR.intersect(origR)) {
    657                     antifillrect(newR, blitter);
    658                 }
    659                 clipper.next();
    660             }
    661         }
    662     } else {
    663         antifillrect(origR, blitter);
    664     }
    665 }
    666 
    667 void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
    668                           SkBlitter* blitter) {
    669     if (clip.isBW()) {
    670         AntiFillRect(r, &clip.bwRgn(), blitter);
    671     } else {
    672         SkAAClipBlitterWrapper wrap(clip, blitter);
    673         AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
    674     }
    675 }
    676 
    677 #endif // SK_SCALAR_IS_FLOAT
    678 
    679 ///////////////////////////////////////////////////////////////////////////////
    680 
    681 #define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
    682 
    683 // calls blitRect() if the rectangle is non-empty
    684 static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
    685     if (L < R && T < B) {
    686         blitter->blitRect(L, T, R - L, B - T);
    687     }
    688 }
    689 
    690 static inline FDot8 SkScalarToFDot8(SkScalar x) {
    691 #ifdef SK_SCALAR_IS_FLOAT
    692     return (int)(x * 256);
    693 #else
    694     return x >> 8;
    695 #endif
    696 }
    697 
    698 static inline int FDot8Floor(FDot8 x) {
    699     return x >> 8;
    700 }
    701 
    702 static inline int FDot8Ceil(FDot8 x) {
    703     return (x + 0xFF) >> 8;
    704 }
    705 
    706 // 1 - (1 - a)*(1 - b)
    707 static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
    708     // need precise rounding (not just SkAlphaMul) so that values like
    709     // a=228, b=252 don't overflow the result
    710     return SkToU8(a + b - SkAlphaMulRound(a, b));
    711 }
    712 
    713 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
    714                            SkBlitter* blitter) {
    715     SkASSERT(L < R);
    716 
    717     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
    718         blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
    719         return;
    720     }
    721 
    722     int left = L >> 8;
    723     if (L & 0xFF) {
    724         blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
    725         left += 1;
    726     }
    727 
    728     int rite = R >> 8;
    729     int width = rite - left;
    730     if (width > 0) {
    731         call_hline_blitter(blitter, left, top, width, alpha);
    732     }
    733 
    734     if (R & 0xFF) {
    735         blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
    736     }
    737 }
    738 
    739 static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
    740                             SkBlitter* blitter) {
    741     SkASSERT(L < R && T < B);
    742 
    743     int top = T >> 8;
    744     if (top == ((B - 1) >> 8)) {   // just one scanline high
    745         inner_scanline(L, top, R, B - T, blitter);
    746         return;
    747     }
    748 
    749     if (T & 0xFF) {
    750         inner_scanline(L, top, R, T & 0xFF, blitter);
    751         top += 1;
    752     }
    753 
    754     int bot = B >> 8;
    755     int height = bot - top;
    756     if (height > 0) {
    757         if (L & 0xFF) {
    758             blitter->blitV(L >> 8, top, height, L & 0xFF);
    759         }
    760         if (R & 0xFF) {
    761             blitter->blitV(R >> 8, top, height, ~R & 0xFF);
    762         }
    763     }
    764 
    765     if (B & 0xFF) {
    766         inner_scanline(L, bot, R, ~B & 0xFF, blitter);
    767     }
    768 }
    769 
    770 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
    771                            const SkRegion* clip, SkBlitter* blitter) {
    772     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
    773 
    774     SkScalar rx = SkScalarHalf(strokeSize.fX);
    775     SkScalar ry = SkScalarHalf(strokeSize.fY);
    776 
    777     // outset by the radius
    778     FDot8 L = SkScalarToFDot8(r.fLeft - rx);
    779     FDot8 T = SkScalarToFDot8(r.fTop - ry);
    780     FDot8 R = SkScalarToFDot8(r.fRight + rx);
    781     FDot8 B = SkScalarToFDot8(r.fBottom + ry);
    782 
    783     SkIRect outer;
    784     // set outer to the outer rect of the outer section
    785     outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
    786 
    787     SkBlitterClipper clipper;
    788     if (clip) {
    789         if (clip->quickReject(outer)) {
    790             return;
    791         }
    792         if (!clip->contains(outer)) {
    793             blitter = clipper.apply(blitter, clip, &outer);
    794         }
    795         // now we can ignore clip for the rest of the function
    796     }
    797 
    798     // stroke the outer hull
    799     antifilldot8(L, T, R, B, blitter, false);
    800 
    801     // set outer to the outer rect of the middle section
    802     outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
    803 
    804     // in case we lost a bit with diameter/2
    805     rx = strokeSize.fX - rx;
    806     ry = strokeSize.fY - ry;
    807     // inset by the radius
    808     L = SkScalarToFDot8(r.fLeft + rx);
    809     T = SkScalarToFDot8(r.fTop + ry);
    810     R = SkScalarToFDot8(r.fRight - rx);
    811     B = SkScalarToFDot8(r.fBottom - ry);
    812 
    813     if (L >= R || T >= B) {
    814         fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
    815                       blitter);
    816     } else {
    817         SkIRect inner;
    818         // set inner to the inner rect of the middle section
    819         inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
    820 
    821         // draw the frame in 4 pieces
    822         fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
    823                       blitter);
    824         fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
    825                       blitter);
    826         fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
    827                       blitter);
    828         fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
    829                       blitter);
    830 
    831         // now stroke the inner rect, which is similar to antifilldot8() except that
    832         // it treats the fractional coordinates with the inverse bias (since its
    833         // inner).
    834         innerstrokedot8(L, T, R, B, blitter);
    835     }
    836 }
    837 
    838 void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
    839                            const SkRasterClip& clip, SkBlitter* blitter) {
    840     if (clip.isBW()) {
    841         AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
    842     } else {
    843         SkAAClipBlitterWrapper wrap(clip, blitter);
    844         AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
    845     }
    846 }
    847 
    848