SkScan_Antihair.cpp revision bbbe9ed59ee1d3077fa4e6368a4a7294240a5ec6
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
34static 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
68static 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
89static 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
113static 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
152static 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
172static 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
203typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
204                            SkFixed slope, SkBlitter*, int);
205
206static 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#define SkBITCOUNT(x)   (sizeof(x) << 3)
213
214#if 1
215// returns high-bit set iff x==0x8000...
216static inline int bad_int(int x) {
217    return x & -x;
218}
219
220static int any_bad_ints(int a, int b, int c, int d) {
221    return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
222}
223#else
224static inline int good_int(int x) {
225    return x ^ (1 << (SkBITCOUNT(x) - 1));
226}
227
228static int any_bad_ints(int a, int b, int c, int d) {
229    return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
230}
231#endif
232
233static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
234                             const SkIRect* clip, SkBlitter* blitter) {
235    // check for integer NaN (0x80000000) which we can't handle (can't negate it)
236    // It appears typically from a huge float (inf or nan) being converted to int.
237    // If we see it, just don't draw.
238    if (any_bad_ints(x0, y0, x1, y1)) {
239        return;
240    }
241
242    if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
243        /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
244            precise, but avoids overflowing the intermediate result if the
245            values are huge. A better fix might be to clip the original pts
246            directly (i.e. do the divide), so we don't spend time subdividing
247            huge lines at all.
248         */
249        int hx = (x0 >> 1) + (x1 >> 1);
250        int hy = (y0 >> 1) + (y1 >> 1);
251        do_anti_hairline(x0, y0, hx, hy, clip, blitter);
252        do_anti_hairline(hx, hy, x1, y1, clip, blitter);
253        return;
254    }
255
256    int         scaleStart, scaleStop;
257    int         istart, istop;
258    SkFixed     fstart, slope;
259    LineProc    proc;
260
261    if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
262        if (x0 > x1) {    // we want to go left-to-right
263            SkTSwap<SkFDot6>(x0, x1);
264            SkTSwap<SkFDot6>(y0, y1);
265        }
266
267        istart = SkFDot6Floor(x0);
268        istop = SkFDot6Ceil(x1);
269        fstart = SkFDot6ToFixed(y0);
270        if (y0 == y1) {   // completely horizontal, take fast case
271            slope = 0;
272            proc = hline;
273        } else {
274            slope = fastfixdiv(y1 - y0, x1 - x0);
275            SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
276            fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
277            proc = horish;
278        }
279
280        SkASSERT(istop > istart);
281        if (istop - istart == 1) {
282            scaleStart = x1 - x0;
283            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
284            scaleStop = 0;
285        } else {
286            scaleStart = 64 - (x0 & 63);
287            scaleStop = x1 & 63;
288        }
289
290        if (clip){
291            if (istart >= clip->fRight || istop <= clip->fLeft) {
292                return;
293            }
294            if (istart < clip->fLeft) {
295                fstart += slope * (clip->fLeft - istart);
296                istart = clip->fLeft;
297                scaleStart = 64;
298            }
299            if (istop > clip->fRight) {
300                istop = clip->fRight;
301                scaleStop = 0;  // so we don't draw this last column
302            }
303
304            SkASSERT(istart <= istop);
305            if (istart == istop) {
306                return;
307            }
308            // now test if our Y values are completely inside the clip
309            int top, bottom;
310            if (slope >= 0) { // T2B
311                top = SkFixedFloor(fstart - SK_FixedHalf);
312                bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
313            } else {           // B2T
314                bottom = SkFixedCeil(fstart + SK_FixedHalf);
315                top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
316            }
317#ifdef OUTSET_BEFORE_CLIP_TEST
318            top -= 1;
319            bottom += 1;
320#endif
321            if (top >= clip->fBottom || bottom <= clip->fTop) {
322                return;
323            }
324            if (clip->fTop <= top && clip->fBottom >= bottom) {
325                clip = NULL;
326            }
327        }
328    } else {   // mostly vertical
329        if (y0 > y1) {  // we want to go top-to-bottom
330            SkTSwap<SkFDot6>(x0, x1);
331            SkTSwap<SkFDot6>(y0, y1);
332        }
333
334        istart = SkFDot6Floor(y0);
335        istop = SkFDot6Ceil(y1);
336        fstart = SkFDot6ToFixed(x0);
337        if (x0 == x1) {
338            if (y0 == y1) { // are we zero length?
339                return;     // nothing to do
340            }
341            slope = 0;
342            proc = vline;
343        } else {
344            slope = fastfixdiv(x1 - x0, y1 - y0);
345            SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
346            fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
347            proc = vertish;
348        }
349
350        SkASSERT(istop > istart);
351        if (istop - istart == 1) {
352            scaleStart = y1 - y0;
353            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
354            scaleStop = 0;
355        } else {
356            scaleStart = 64 - (y0 & 63);
357            scaleStop = y1 & 63;
358        }
359
360        if (clip) {
361            if (istart >= clip->fBottom || istop <= clip->fTop) {
362                return;
363            }
364            if (istart < clip->fTop) {
365                fstart += slope * (clip->fTop - istart);
366                istart = clip->fTop;
367                scaleStart = 64;
368            }
369            if (istop > clip->fBottom) {
370                istop = clip->fBottom;
371                scaleStop = 0;  // so we don't draw this last row
372            }
373
374            SkASSERT(istart <= istop);
375            if (istart == istop)
376                return;
377
378            // now test if our X values are completely inside the clip
379            int left, right;
380            if (slope >= 0) { // L2R
381                left = SkFixedFloor(fstart - SK_FixedHalf);
382                right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
383            } else {           // R2L
384                right = SkFixedCeil(fstart + SK_FixedHalf);
385                left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
386            }
387#ifdef OUTSET_BEFORE_CLIP_TEST
388            left -= 1;
389            right += 1;
390#endif
391            if (left >= clip->fRight || right <= clip->fLeft) {
392                return;
393            }
394            if (clip->fLeft <= left && clip->fRight >= right) {
395                clip = NULL;
396            }
397        }
398    }
399
400    SkRectClipBlitter   rectClipper;
401    if (clip) {
402        rectClipper.init(blitter, *clip);
403        blitter = &rectClipper;
404    }
405
406    fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
407    istart += 1;
408    int fullSpans = istop - istart - (scaleStop > 0);
409    if (fullSpans > 0) {
410        fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
411    }
412    if (scaleStop > 0) {
413        proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
414    }
415}
416
417void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
418                             const SkRegion* clip, SkBlitter* blitter) {
419    if (clip && clip->isEmpty()) {
420        return;
421    }
422
423    SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
424
425#ifdef TEST_GAMMA
426    build_gamma_table();
427#endif
428
429    SkPoint pts[2] = { pt0, pt1 };
430
431    if (clip) {
432        SkRect clipBounds;
433        clipBounds.set(clip->getBounds());
434        /*  We perform integral clipping later on, but we do a scalar clip first
435            to ensure that our coordinates are expressible in fixed/integers.
436
437            antialiased hairlines can draw up to 1/2 of a pixel outside of
438            their bounds, so we need to outset the clip before calling the
439            clipper. To make the numerics safer, we outset by a whole pixel,
440            since the 1/2 pixel boundary is important to the antihair blitter,
441            we don't want to risk numerical fate by chopping on that edge.
442         */
443        clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
444
445        if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
446            return;
447        }
448    }
449
450    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
451    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
452    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
453    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
454
455    if (clip) {
456        SkFDot6 left = SkMin32(x0, x1);
457        SkFDot6 top = SkMin32(y0, y1);
458        SkFDot6 right = SkMax32(x0, x1);
459        SkFDot6 bottom = SkMax32(y0, y1);
460        SkIRect ir;
461
462        ir.set( SkFDot6Floor(left) - 1,
463                SkFDot6Floor(top) - 1,
464                SkFDot6Ceil(right) + 1,
465                SkFDot6Ceil(bottom) + 1);
466
467        if (clip->quickReject(ir)) {
468            return;
469        }
470        if (!clip->quickContains(ir)) {
471            SkRegion::Cliperator iter(*clip, ir);
472            const SkIRect*       r = &iter.rect();
473
474            while (!iter.done()) {
475                do_anti_hairline(x0, y0, x1, y1, r, blitter);
476                iter.next();
477            }
478            return;
479        }
480        // fall through to no-clip case
481    }
482    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
483}
484
485void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
486                          SkBlitter* blitter) {
487    SkPoint p0, p1;
488
489    p0.set(rect.fLeft, rect.fTop);
490    p1.set(rect.fRight, rect.fTop);
491    SkScan::AntiHairLine(p0, p1, clip, blitter);
492    p0.set(rect.fRight, rect.fBottom);
493    SkScan::AntiHairLine(p0, p1, clip, blitter);
494    p1.set(rect.fLeft, rect.fBottom);
495    SkScan::AntiHairLine(p0, p1, clip, blitter);
496    p0.set(rect.fLeft, rect.fTop);
497    SkScan::AntiHairLine(p0, p1, clip, blitter);
498}
499
500///////////////////////////////////////////////////////////////////////////////
501
502typedef int FDot8;  // 24.8 integer fixed point
503
504static inline FDot8 SkFixedToFDot8(SkFixed x) {
505    return (x + 0x80) >> 8;
506}
507
508static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
509                        SkBlitter* blitter) {
510    SkASSERT(L < R);
511
512    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
513        blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
514        return;
515    }
516
517    int left = L >> 8;
518
519    if (L & 0xFF) {
520        blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
521        left += 1;
522    }
523
524    int rite = R >> 8;
525    int width = rite - left;
526    if (width > 0) {
527        call_hline_blitter(blitter, left, top, width, alpha);
528    }
529    if (R & 0xFF) {
530        blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
531    }
532}
533
534static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
535                         bool fillInner) {
536    // check for empty now that we're in our reduced precision space
537    if (L >= R || T >= B) {
538        return;
539    }
540    int top = T >> 8;
541    if (top == ((B - 1) >> 8)) {   // just one scanline high
542        do_scanline(L, top, R, B - T - 1, blitter);
543        return;
544    }
545
546    if (T & 0xFF) {
547        do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
548        top += 1;
549    }
550
551    int bot = B >> 8;
552    int height = bot - top;
553    if (height > 0) {
554        int left = L >> 8;
555        if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
556            blitter->blitV(left, top, height, R - L - 1);
557        } else {
558            if (L & 0xFF) {
559                blitter->blitV(left, top, height, 256 - (L & 0xFF));
560                left += 1;
561            }
562            int rite = R >> 8;
563            int width = rite - left;
564            if (width > 0 && fillInner) {
565                blitter->blitRect(left, top, width, height);
566            }
567            if (R & 0xFF) {
568                blitter->blitV(rite, top, height, R & 0xFF);
569            }
570        }
571    }
572
573    if (B & 0xFF) {
574        do_scanline(L, bot, R, B & 0xFF, blitter);
575    }
576}
577
578static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
579    antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
580                 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
581                 blitter, true);
582}
583
584///////////////////////////////////////////////////////////////////////////////
585
586void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
587                          SkBlitter* blitter) {
588    if (NULL == clip) {
589        antifillrect(xr, blitter);
590    } else {
591        SkIRect outerBounds;
592        XRect_roundOut(xr, &outerBounds);
593
594        if (clip->isRect()) {
595            const SkIRect& clipBounds = clip->getBounds();
596
597            if (clipBounds.contains(outerBounds)) {
598                antifillrect(xr, blitter);
599            } else {
600                SkXRect tmpR;
601                // this keeps our original edges fractional
602                XRect_set(&tmpR, clipBounds);
603                if (tmpR.intersect(xr)) {
604                    antifillrect(tmpR, blitter);
605                }
606            }
607        } else {
608            SkRegion::Cliperator clipper(*clip, outerBounds);
609            const SkIRect&       rr = clipper.rect();
610
611            while (!clipper.done()) {
612                SkXRect  tmpR;
613
614                // this keeps our original edges fractional
615                XRect_set(&tmpR, rr);
616                if (tmpR.intersect(xr)) {
617                    antifillrect(tmpR, blitter);
618                }
619                clipper.next();
620            }
621        }
622    }
623}
624
625void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
626                           SkBlitter* blitter) {
627    if (clip.isBW()) {
628        AntiFillXRect(xr, &clip.bwRgn(), blitter);
629    } else {
630        SkIRect outerBounds;
631        XRect_roundOut(xr, &outerBounds);
632
633        if (clip.quickContains(outerBounds)) {
634            AntiFillXRect(xr, NULL, blitter);
635        } else {
636            SkAAClipBlitterWrapper wrapper(clip, blitter);
637            blitter = wrapper.getBlitter();
638
639            AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
640        }
641    }
642}
643
644#ifdef SK_SCALAR_IS_FLOAT
645
646/*  This guy takes a float-rect, but with the key improvement that it has
647    already been clipped, so we know that it is safe to convert it into a
648    XRect (fixedpoint), as it won't overflow.
649*/
650static void antifillrect(const SkRect& r, SkBlitter* blitter) {
651    SkXRect xr;
652
653    XRect_set(&xr, r);
654    antifillrect(xr, blitter);
655}
656
657/*  We repeat the clipping logic of AntiFillXRect because the float rect might
658    overflow if we blindly converted it to an XRect. This sucks that we have to
659    repeat the clipping logic, but I don't see how to share the code/logic.
660
661    We clip r (as needed) into one or more (smaller) float rects, and then pass
662    those to our version of antifillrect, which converts it into an XRect and
663    then calls the blit.
664*/
665void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
666                          SkBlitter* blitter) {
667    if (clip) {
668        SkRect newR;
669        newR.set(clip->getBounds());
670        if (!newR.intersect(origR)) {
671            return;
672        }
673
674        SkIRect outerBounds;
675        newR.roundOut(&outerBounds);
676
677        if (clip->isRect()) {
678            antifillrect(newR, blitter);
679        } else {
680            SkRegion::Cliperator clipper(*clip, outerBounds);
681            while (!clipper.done()) {
682                newR.set(clipper.rect());
683                if (newR.intersect(origR)) {
684                    antifillrect(newR, blitter);
685                }
686                clipper.next();
687            }
688        }
689    } else {
690        antifillrect(origR, blitter);
691    }
692}
693
694void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
695                          SkBlitter* blitter) {
696    if (clip.isBW()) {
697        AntiFillRect(r, &clip.bwRgn(), blitter);
698    } else {
699        SkAAClipBlitterWrapper wrap(clip, blitter);
700        AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
701    }
702}
703
704#endif // SK_SCALAR_IS_FLOAT
705
706///////////////////////////////////////////////////////////////////////////////
707
708#define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
709
710// calls blitRect() if the rectangle is non-empty
711static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
712    if (L < R && T < B) {
713        blitter->blitRect(L, T, R - L, B - T);
714    }
715}
716
717static inline FDot8 SkScalarToFDot8(SkScalar x) {
718#ifdef SK_SCALAR_IS_FLOAT
719    return (int)(x * 256);
720#else
721    return x >> 8;
722#endif
723}
724
725static inline int FDot8Floor(FDot8 x) {
726    return x >> 8;
727}
728
729static inline int FDot8Ceil(FDot8 x) {
730    return (x + 0xFF) >> 8;
731}
732
733// 1 - (1 - a)*(1 - b)
734static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
735    // need precise rounding (not just SkAlphaMul) so that values like
736    // a=228, b=252 don't overflow the result
737    return SkToU8(a + b - SkAlphaMulRound(a, b));
738}
739
740static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
741                           SkBlitter* blitter) {
742    SkASSERT(L < R);
743
744    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
745        blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
746        return;
747    }
748
749    int left = L >> 8;
750    if (L & 0xFF) {
751        blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
752        left += 1;
753    }
754
755    int rite = R >> 8;
756    int width = rite - left;
757    if (width > 0) {
758        call_hline_blitter(blitter, left, top, width, alpha);
759    }
760
761    if (R & 0xFF) {
762        blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
763    }
764}
765
766static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
767                            SkBlitter* blitter) {
768    SkASSERT(L < R && T < B);
769
770    int top = T >> 8;
771    if (top == ((B - 1) >> 8)) {   // just one scanline high
772        inner_scanline(L, top, R, B - T, blitter);
773        return;
774    }
775
776    if (T & 0xFF) {
777        inner_scanline(L, top, R, T & 0xFF, blitter);
778        top += 1;
779    }
780
781    int bot = B >> 8;
782    int height = bot - top;
783    if (height > 0) {
784        if (L & 0xFF) {
785            blitter->blitV(L >> 8, top, height, L & 0xFF);
786        }
787        if (R & 0xFF) {
788            blitter->blitV(R >> 8, top, height, ~R & 0xFF);
789        }
790    }
791
792    if (B & 0xFF) {
793        inner_scanline(L, bot, R, ~B & 0xFF, blitter);
794    }
795}
796
797void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
798                           const SkRegion* clip, SkBlitter* blitter) {
799    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
800
801    SkScalar rx = SkScalarHalf(strokeSize.fX);
802    SkScalar ry = SkScalarHalf(strokeSize.fY);
803
804    // outset by the radius
805    FDot8 L = SkScalarToFDot8(r.fLeft - rx);
806    FDot8 T = SkScalarToFDot8(r.fTop - ry);
807    FDot8 R = SkScalarToFDot8(r.fRight + rx);
808    FDot8 B = SkScalarToFDot8(r.fBottom + ry);
809
810    SkIRect outer;
811    // set outer to the outer rect of the outer section
812    outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
813
814    SkBlitterClipper clipper;
815    if (clip) {
816        if (clip->quickReject(outer)) {
817            return;
818        }
819        if (!clip->contains(outer)) {
820            blitter = clipper.apply(blitter, clip, &outer);
821        }
822        // now we can ignore clip for the rest of the function
823    }
824
825    // stroke the outer hull
826    antifilldot8(L, T, R, B, blitter, false);
827
828    // set outer to the outer rect of the middle section
829    outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
830
831    // in case we lost a bit with diameter/2
832    rx = strokeSize.fX - rx;
833    ry = strokeSize.fY - ry;
834    // inset by the radius
835    L = SkScalarToFDot8(r.fLeft + rx);
836    T = SkScalarToFDot8(r.fTop + ry);
837    R = SkScalarToFDot8(r.fRight - rx);
838    B = SkScalarToFDot8(r.fBottom - ry);
839
840    if (L >= R || T >= B) {
841        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
842                      blitter);
843    } else {
844        SkIRect inner;
845        // set inner to the inner rect of the middle section
846        inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
847
848        // draw the frame in 4 pieces
849        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
850                      blitter);
851        fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
852                      blitter);
853        fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
854                      blitter);
855        fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
856                      blitter);
857
858        // now stroke the inner rect, which is similar to antifilldot8() except that
859        // it treats the fractional coordinates with the inverse bias (since its
860        // inner).
861        innerstrokedot8(L, T, R, B, blitter);
862    }
863}
864
865void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
866                           const SkRasterClip& clip, SkBlitter* blitter) {
867    if (clip.isBW()) {
868        AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
869    } else {
870        SkAAClipBlitterWrapper wrap(clip, blitter);
871        AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
872    }
873}
874
875