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
212static 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
390void 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
458void 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
475typedef int FDot8;  // 24.8 integer fixed point
476
477static inline FDot8 SkFixedToFDot8(SkFixed x) {
478    return (x + 0x80) >> 8;
479}
480
481static 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
507static 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
551static 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
559void 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
598void 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*/
623static 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*/
638void 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
667void 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
684static 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
690static 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
698static inline int FDot8Floor(FDot8 x) {
699    return x >> 8;
700}
701
702static inline int FDot8Ceil(FDot8 x) {
703    return (x + 0xFF) >> 8;
704}
705
706// 1 - (1 - a)*(1 - b)
707static 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
713static 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
739static 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
770void 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
838void 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