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