SkScan_Antihair.cpp revision 0e51577a14f903ffeafa117a75954baeb173ffb9
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
89class SkAntiHairBlitter {
90public:
91    SkAntiHairBlitter() : fBlitter(NULL) {}
92    virtual ~SkAntiHairBlitter() {}
93
94    SkBlitter* getBlitter() const { return fBlitter; }
95
96    void setup(SkBlitter* blitter) {
97        fBlitter = blitter;
98    }
99
100    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
101    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
102
103private:
104    SkBlitter*  fBlitter;
105};
106
107class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
108public:
109    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
110        fy += SK_Fixed1/2;
111
112        int y = fy >> 16;
113        uint8_t  a = (uint8_t)(fy >> 8);
114
115        // lower line
116        unsigned ma = SmallDot6Scale(a, mod64);
117        if (ma) {
118            call_hline_blitter(this->getBlitter(), x, y, 1, ma);
119        }
120
121        // upper line
122        ma = SmallDot6Scale(255 - a, mod64);
123        if (ma) {
124            call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
125        }
126
127        return fy - SK_Fixed1/2;
128    }
129
130    virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
131                             SkFixed slope) SK_OVERRIDE {
132        SkASSERT(x < stopx);
133        int count = stopx - x;
134        fy += SK_Fixed1/2;
135
136        int y = fy >> 16;
137        uint8_t  a = (uint8_t)(fy >> 8);
138
139        // lower line
140        if (a) {
141            call_hline_blitter(this->getBlitter(), x, y, count, a);
142        }
143
144        // upper line
145        a = 255 - a;
146        if (a) {
147            call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
148        }
149
150        return fy - SK_Fixed1/2;
151    }
152};
153
154class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
155public:
156    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
157        int16_t runs[2];
158        uint8_t  aa[1];
159
160        runs[0] = 1;
161        runs[1] = 0;
162
163        fy += SK_Fixed1/2;
164        SkBlitter* blitter = this->getBlitter();
165
166        int lower_y = fy >> 16;
167        uint8_t  a = (uint8_t)(fy >> 8);
168        unsigned ma = SmallDot6Scale(a, mod64);
169        if (ma) {
170            aa[0] = ApplyGamma(gamma, ma);
171            blitter->blitAntiH(x, lower_y, aa, runs);
172            // the clipping blitters might edit runs, but should not affect us
173            SkASSERT(runs[0] == 1);
174            SkASSERT(runs[1] == 0);
175        }
176        ma = SmallDot6Scale(255 - a, mod64);
177        if (ma) {
178            aa[0] = ApplyGamma(gamma, ma);
179            blitter->blitAntiH(x, lower_y - 1, aa, runs);
180            // the clipping blitters might edit runs, but should not affect us
181            SkASSERT(runs[0] == 1);
182            SkASSERT(runs[1] == 0);
183        }
184        fy += dy;
185
186        return fy - SK_Fixed1/2;
187    }
188
189    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
190        SkASSERT(x < stopx);
191
192        int16_t runs[2];
193        uint8_t  aa[1];
194
195        runs[0] = 1;
196        runs[1] = 0;
197
198        fy += SK_Fixed1/2;
199        SkBlitter* blitter = this->getBlitter();
200        do {
201            int lower_y = fy >> 16;
202            uint8_t  a = (uint8_t)(fy >> 8);
203            if (a) {
204                aa[0] = a;
205                blitter->blitAntiH(x, lower_y, aa, runs);
206                // the clipping blitters might edit runs, but should not affect us
207                SkASSERT(runs[0] == 1);
208                SkASSERT(runs[1] == 0);
209            }
210            a = 255 - a;
211            if (a) {
212                aa[0] = a;
213                blitter->blitAntiH(x, lower_y - 1, aa, runs);
214                // the clipping blitters might edit runs, but should not affect us
215                SkASSERT(runs[0] == 1);
216                SkASSERT(runs[1] == 0);
217            }
218            fy += dy;
219        } while (++x < stopx);
220
221        return fy - SK_Fixed1/2;
222    }
223};
224
225class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
226public:
227    virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
228        SkASSERT(0 == dx);
229        fx += SK_Fixed1/2;
230
231        int x = fx >> 16;
232        int a = (uint8_t)(fx >> 8);
233
234        unsigned ma = SmallDot6Scale(a, mod64);
235        if (ma) {
236            this->getBlitter()->blitV(x, y, 1, ma);
237        }
238        ma = SmallDot6Scale(255 - a, mod64);
239        if (ma) {
240            this->getBlitter()->blitV(x - 1, y, 1, ma);
241        }
242
243        return fx - SK_Fixed1/2;
244    }
245
246    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
247        SkASSERT(y < stopy);
248        SkASSERT(0 == dx);
249        fx += SK_Fixed1/2;
250
251        int x = fx >> 16;
252        int a = (uint8_t)(fx >> 8);
253
254        if (a) {
255            this->getBlitter()->blitV(x, y, stopy - y, a);
256        }
257        a = 255 - a;
258        if (a) {
259            this->getBlitter()->blitV(x - 1, y, stopy - y, a);
260        }
261
262        return fx - SK_Fixed1/2;
263    }
264};
265
266class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
267public:
268    virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
269        int16_t runs[3];
270        uint8_t  aa[2];
271
272        runs[0] = 1;
273        runs[2] = 0;
274
275        fx += SK_Fixed1/2;
276        int x = fx >> 16;
277        uint8_t  a = (uint8_t)(fx >> 8);
278
279        aa[0] = SmallDot6Scale(255 - a, mod64);
280        aa[1] = SmallDot6Scale(a, mod64);
281        // the clippng blitters might overwrite this guy, so we have to reset it each time
282        runs[1] = 1;
283        this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
284        // the clipping blitters might edit runs, but should not affect us
285        SkASSERT(runs[0] == 1);
286        SkASSERT(runs[2] == 0);
287        fx += dx;
288
289        return fx - SK_Fixed1/2;
290    }
291
292    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
293        SkASSERT(y < stopy);
294        int16_t runs[3];
295        uint8_t  aa[2];
296
297        runs[0] = 1;
298        runs[2] = 0;
299
300        fx += SK_Fixed1/2;
301        do {
302            int x = fx >> 16;
303            uint8_t  a = (uint8_t)(fx >> 8);
304
305            aa[0] = 255 - a;
306            aa[1] = a;
307            // the clippng blitters might overwrite this guy, so we have to reset it each time
308            runs[1] = 1;
309            this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
310            // the clipping blitters might edit runs, but should not affect us
311            SkASSERT(runs[0] == 1);
312            SkASSERT(runs[2] == 0);
313            fx += dx;
314        } while (++y < stopy);
315
316        return fx - SK_Fixed1/2;
317    }
318};
319
320static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
321    SkASSERT((a << 16 >> 16) == a);
322    SkASSERT(b != 0);
323    return (a << 16) / b;
324}
325
326#define SkBITCOUNT(x)   (sizeof(x) << 3)
327
328#if 1
329// returns high-bit set iff x==0x8000...
330static inline int bad_int(int x) {
331    return x & -x;
332}
333
334static int any_bad_ints(int a, int b, int c, int d) {
335    return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
336}
337#else
338static inline int good_int(int x) {
339    return x ^ (1 << (SkBITCOUNT(x) - 1));
340}
341
342static int any_bad_ints(int a, int b, int c, int d) {
343    return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
344}
345#endif
346
347#ifdef SK_DEBUG
348static bool canConvertFDot6ToFixed(SkFDot6 x) {
349    const int maxDot6 = SK_MaxS32 >> (16 - 6);
350    return SkAbs32(x) <= maxDot6;
351}
352#endif
353
354/*
355 *  We want the fractional part of ordinate, but we want multiples of 64 to
356 *  return 64, not 0, so we can't just say (ordinate & 63).
357 *  We basically want to compute those bits, and if they're 0, return 64.
358 *  We can do that w/o a branch with an extra sub and add.
359 */
360static int contribution_64(SkFDot6 ordinate) {
361#if 0
362    int result = ordinate & 63;
363    if (0 == result) {
364        result = 64;
365    }
366#else
367    int result = ((ordinate - 1) & 63) + 1;
368#endif
369    SkASSERT(result > 0 && result <= 64);
370    return result;
371}
372
373static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
374                             const SkIRect* clip, SkBlitter* blitter) {
375    // check for integer NaN (0x80000000) which we can't handle (can't negate it)
376    // It appears typically from a huge float (inf or nan) being converted to int.
377    // If we see it, just don't draw.
378    if (any_bad_ints(x0, y0, x1, y1)) {
379        return;
380    }
381
382    // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
383    // (in dot6 format)
384    SkASSERT(canConvertFDot6ToFixed(x0));
385    SkASSERT(canConvertFDot6ToFixed(y0));
386    SkASSERT(canConvertFDot6ToFixed(x1));
387    SkASSERT(canConvertFDot6ToFixed(y1));
388
389    if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
390        /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
391            precise, but avoids overflowing the intermediate result if the
392            values are huge. A better fix might be to clip the original pts
393            directly (i.e. do the divide), so we don't spend time subdividing
394            huge lines at all.
395         */
396        int hx = (x0 >> 1) + (x1 >> 1);
397        int hy = (y0 >> 1) + (y1 >> 1);
398        do_anti_hairline(x0, y0, hx, hy, clip, blitter);
399        do_anti_hairline(hx, hy, x1, y1, clip, blitter);
400        return;
401    }
402
403    int         scaleStart, scaleStop;
404    int         istart, istop;
405    SkFixed     fstart, slope;
406
407    HLine_SkAntiHairBlitter     hline_blitter;
408    Horish_SkAntiHairBlitter    horish_blitter;
409    VLine_SkAntiHairBlitter     vline_blitter;
410    Vertish_SkAntiHairBlitter   vertish_blitter;
411    SkAntiHairBlitter*          hairBlitter = NULL;
412
413    if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
414        if (x0 > x1) {    // we want to go left-to-right
415            SkTSwap<SkFDot6>(x0, x1);
416            SkTSwap<SkFDot6>(y0, y1);
417        }
418
419        istart = SkFDot6Floor(x0);
420        istop = SkFDot6Ceil(x1);
421        fstart = SkFDot6ToFixed(y0);
422        if (y0 == y1) {   // completely horizontal, take fast case
423            slope = 0;
424            hairBlitter = &hline_blitter;
425        } else {
426            slope = fastfixdiv(y1 - y0, x1 - x0);
427            SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
428            fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
429            hairBlitter = &horish_blitter;
430        }
431
432        SkASSERT(istop > istart);
433        if (istop - istart == 1) {
434            // we are within a single pixel
435            scaleStart = x1 - x0;
436            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
437            scaleStop = 0;
438        } else {
439            scaleStart = 64 - (x0 & 63);
440            scaleStop = x1 & 63;
441        }
442
443        if (clip){
444            if (istart >= clip->fRight || istop <= clip->fLeft) {
445                return;
446            }
447            if (istart < clip->fLeft) {
448                fstart += slope * (clip->fLeft - istart);
449                istart = clip->fLeft;
450                scaleStart = 64;
451                if (istop - istart == 1) {
452                    // we are within a single pixel
453                    scaleStart = contribution_64(x1);
454                    scaleStop = 0;
455                }
456            }
457            if (istop > clip->fRight) {
458                istop = clip->fRight;
459                scaleStop = 0;  // so we don't draw this last column
460            }
461
462            SkASSERT(istart <= istop);
463            if (istart == istop) {
464                return;
465            }
466            // now test if our Y values are completely inside the clip
467            int top, bottom;
468            if (slope >= 0) { // T2B
469                top = SkFixedFloor(fstart - SK_FixedHalf);
470                bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
471            } else {           // B2T
472                bottom = SkFixedCeil(fstart + SK_FixedHalf);
473                top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
474            }
475#ifdef OUTSET_BEFORE_CLIP_TEST
476            top -= 1;
477            bottom += 1;
478#endif
479            if (top >= clip->fBottom || bottom <= clip->fTop) {
480                return;
481            }
482            if (clip->fTop <= top && clip->fBottom >= bottom) {
483                clip = NULL;
484            }
485        }
486    } else {   // mostly vertical
487        if (y0 > y1) {  // we want to go top-to-bottom
488            SkTSwap<SkFDot6>(x0, x1);
489            SkTSwap<SkFDot6>(y0, y1);
490        }
491
492        istart = SkFDot6Floor(y0);
493        istop = SkFDot6Ceil(y1);
494        fstart = SkFDot6ToFixed(x0);
495        if (x0 == x1) {
496            if (y0 == y1) { // are we zero length?
497                return;     // nothing to do
498            }
499            slope = 0;
500            hairBlitter = &vline_blitter;
501        } else {
502            slope = fastfixdiv(x1 - x0, y1 - y0);
503            SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
504            fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
505            hairBlitter = &vertish_blitter;
506        }
507
508        SkASSERT(istop > istart);
509        if (istop - istart == 1) {
510            // we are within a single pixel
511            scaleStart = y1 - y0;
512            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
513            scaleStop = 0;
514        } else {
515            scaleStart = 64 - (y0 & 63);
516            scaleStop = y1 & 63;
517        }
518
519        if (clip) {
520            if (istart >= clip->fBottom || istop <= clip->fTop) {
521                return;
522            }
523            if (istart < clip->fTop) {
524                fstart += slope * (clip->fTop - istart);
525                istart = clip->fTop;
526                scaleStart = 64;
527                if (istop - istart == 1) {
528                    // we are within a single pixel
529                    scaleStart = contribution_64(y1);
530                    scaleStop = 0;
531                }
532            }
533            if (istop > clip->fBottom) {
534                istop = clip->fBottom;
535                scaleStop = 0;  // so we don't draw this last row
536            }
537
538            SkASSERT(istart <= istop);
539            if (istart == istop)
540                return;
541
542            // now test if our X values are completely inside the clip
543            int left, right;
544            if (slope >= 0) { // L2R
545                left = SkFixedFloor(fstart - SK_FixedHalf);
546                right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
547            } else {           // R2L
548                right = SkFixedCeil(fstart + SK_FixedHalf);
549                left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
550            }
551#ifdef OUTSET_BEFORE_CLIP_TEST
552            left -= 1;
553            right += 1;
554#endif
555            if (left >= clip->fRight || right <= clip->fLeft) {
556                return;
557            }
558            if (clip->fLeft <= left && clip->fRight >= right) {
559                clip = NULL;
560            }
561        }
562    }
563
564    SkRectClipBlitter   rectClipper;
565    if (clip) {
566        rectClipper.init(blitter, *clip);
567        blitter = &rectClipper;
568    }
569
570    SkASSERT(hairBlitter);
571    hairBlitter->setup(blitter);
572
573#ifdef SK_DEBUG
574    if (scaleStart > 0 && scaleStop > 0) {
575        // be sure we don't draw twice in the same pixel
576        SkASSERT(istart < istop - 1);
577    }
578#endif
579
580    fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
581    istart += 1;
582    int fullSpans = istop - istart - (scaleStop > 0);
583    if (fullSpans > 0) {
584        fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
585    }
586    if (scaleStop > 0) {
587        hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
588    }
589}
590
591void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
592                             const SkRegion* clip, SkBlitter* blitter) {
593    if (clip && clip->isEmpty()) {
594        return;
595    }
596
597    SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
598
599#ifdef TEST_GAMMA
600    build_gamma_table();
601#endif
602
603    SkPoint pts[2] = { pt0, pt1 };
604
605#ifdef SK_SCALAR_IS_FLOAT
606    // We have to pre-clip the line to fit in a SkFixed, so we just chop
607    // the line. TODO find a way to actually draw beyond that range.
608    {
609        SkRect fixedBounds;
610        const SkScalar max = SkIntToScalar(32767);
611        fixedBounds.set(-max, -max, max, max);
612        if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
613            return;
614        }
615    }
616#endif
617
618    if (clip) {
619        SkRect clipBounds;
620        clipBounds.set(clip->getBounds());
621        /*  We perform integral clipping later on, but we do a scalar clip first
622            to ensure that our coordinates are expressible in fixed/integers.
623
624            antialiased hairlines can draw up to 1/2 of a pixel outside of
625            their bounds, so we need to outset the clip before calling the
626            clipper. To make the numerics safer, we outset by a whole pixel,
627            since the 1/2 pixel boundary is important to the antihair blitter,
628            we don't want to risk numerical fate by chopping on that edge.
629         */
630        clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
631
632        if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
633            return;
634        }
635    }
636
637    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
638    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
639    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
640    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
641
642    if (clip) {
643        SkFDot6 left = SkMin32(x0, x1);
644        SkFDot6 top = SkMin32(y0, y1);
645        SkFDot6 right = SkMax32(x0, x1);
646        SkFDot6 bottom = SkMax32(y0, y1);
647        SkIRect ir;
648
649        ir.set( SkFDot6Floor(left) - 1,
650                SkFDot6Floor(top) - 1,
651                SkFDot6Ceil(right) + 1,
652                SkFDot6Ceil(bottom) + 1);
653
654        if (clip->quickReject(ir)) {
655            return;
656        }
657        if (!clip->quickContains(ir)) {
658            SkRegion::Cliperator iter(*clip, ir);
659            const SkIRect*       r = &iter.rect();
660
661            while (!iter.done()) {
662                do_anti_hairline(x0, y0, x1, y1, r, blitter);
663                iter.next();
664            }
665            return;
666        }
667        // fall through to no-clip case
668    }
669    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
670}
671
672void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
673                          SkBlitter* blitter) {
674    SkPoint p0, p1;
675
676    p0.set(rect.fLeft, rect.fTop);
677    p1.set(rect.fRight, rect.fTop);
678    SkScan::AntiHairLine(p0, p1, clip, blitter);
679    p0.set(rect.fRight, rect.fBottom);
680    SkScan::AntiHairLine(p0, p1, clip, blitter);
681    p1.set(rect.fLeft, rect.fBottom);
682    SkScan::AntiHairLine(p0, p1, clip, blitter);
683    p0.set(rect.fLeft, rect.fTop);
684    SkScan::AntiHairLine(p0, p1, clip, blitter);
685}
686
687///////////////////////////////////////////////////////////////////////////////
688
689typedef int FDot8;  // 24.8 integer fixed point
690
691static inline FDot8 SkFixedToFDot8(SkFixed x) {
692    return (x + 0x80) >> 8;
693}
694
695static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
696                        SkBlitter* blitter) {
697    SkASSERT(L < R);
698
699    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
700        blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
701        return;
702    }
703
704    int left = L >> 8;
705
706    if (L & 0xFF) {
707        blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
708        left += 1;
709    }
710
711    int rite = R >> 8;
712    int width = rite - left;
713    if (width > 0) {
714        call_hline_blitter(blitter, left, top, width, alpha);
715    }
716    if (R & 0xFF) {
717        blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
718    }
719}
720
721static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
722                         bool fillInner) {
723    // check for empty now that we're in our reduced precision space
724    if (L >= R || T >= B) {
725        return;
726    }
727    int top = T >> 8;
728    if (top == ((B - 1) >> 8)) {   // just one scanline high
729        do_scanline(L, top, R, B - T - 1, blitter);
730        return;
731    }
732
733    if (T & 0xFF) {
734        do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
735        top += 1;
736    }
737
738    int bot = B >> 8;
739    int height = bot - top;
740    if (height > 0) {
741        int left = L >> 8;
742        if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
743            blitter->blitV(left, top, height, R - L - 1);
744        } else {
745            if (L & 0xFF) {
746                blitter->blitV(left, top, height, 256 - (L & 0xFF));
747                left += 1;
748            }
749            int rite = R >> 8;
750            int width = rite - left;
751            if (width > 0 && fillInner) {
752                blitter->blitRect(left, top, width, height);
753            }
754            if (R & 0xFF) {
755                blitter->blitV(rite, top, height, R & 0xFF);
756            }
757        }
758    }
759
760    if (B & 0xFF) {
761        do_scanline(L, bot, R, B & 0xFF, blitter);
762    }
763}
764
765static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
766    antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
767                 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
768                 blitter, true);
769}
770
771///////////////////////////////////////////////////////////////////////////////
772
773void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
774                          SkBlitter* blitter) {
775    if (NULL == clip) {
776        antifillrect(xr, blitter);
777    } else {
778        SkIRect outerBounds;
779        XRect_roundOut(xr, &outerBounds);
780
781        if (clip->isRect()) {
782            const SkIRect& clipBounds = clip->getBounds();
783
784            if (clipBounds.contains(outerBounds)) {
785                antifillrect(xr, blitter);
786            } else {
787                SkXRect tmpR;
788                // this keeps our original edges fractional
789                XRect_set(&tmpR, clipBounds);
790                if (tmpR.intersect(xr)) {
791                    antifillrect(tmpR, blitter);
792                }
793            }
794        } else {
795            SkRegion::Cliperator clipper(*clip, outerBounds);
796            const SkIRect&       rr = clipper.rect();
797
798            while (!clipper.done()) {
799                SkXRect  tmpR;
800
801                // this keeps our original edges fractional
802                XRect_set(&tmpR, rr);
803                if (tmpR.intersect(xr)) {
804                    antifillrect(tmpR, blitter);
805                }
806                clipper.next();
807            }
808        }
809    }
810}
811
812void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
813                           SkBlitter* blitter) {
814    if (clip.isBW()) {
815        AntiFillXRect(xr, &clip.bwRgn(), blitter);
816    } else {
817        SkIRect outerBounds;
818        XRect_roundOut(xr, &outerBounds);
819
820        if (clip.quickContains(outerBounds)) {
821            AntiFillXRect(xr, NULL, blitter);
822        } else {
823            SkAAClipBlitterWrapper wrapper(clip, blitter);
824            blitter = wrapper.getBlitter();
825
826            AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
827        }
828    }
829}
830
831#ifdef SK_SCALAR_IS_FLOAT
832
833/*  This guy takes a float-rect, but with the key improvement that it has
834    already been clipped, so we know that it is safe to convert it into a
835    XRect (fixedpoint), as it won't overflow.
836*/
837static void antifillrect(const SkRect& r, SkBlitter* blitter) {
838    SkXRect xr;
839
840    XRect_set(&xr, r);
841    antifillrect(xr, blitter);
842}
843
844/*  We repeat the clipping logic of AntiFillXRect because the float rect might
845    overflow if we blindly converted it to an XRect. This sucks that we have to
846    repeat the clipping logic, but I don't see how to share the code/logic.
847
848    We clip r (as needed) into one or more (smaller) float rects, and then pass
849    those to our version of antifillrect, which converts it into an XRect and
850    then calls the blit.
851*/
852void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
853                          SkBlitter* blitter) {
854    if (clip) {
855        SkRect newR;
856        newR.set(clip->getBounds());
857        if (!newR.intersect(origR)) {
858            return;
859        }
860
861        SkIRect outerBounds;
862        newR.roundOut(&outerBounds);
863
864        if (clip->isRect()) {
865            antifillrect(newR, blitter);
866        } else {
867            SkRegion::Cliperator clipper(*clip, outerBounds);
868            while (!clipper.done()) {
869                newR.set(clipper.rect());
870                if (newR.intersect(origR)) {
871                    antifillrect(newR, blitter);
872                }
873                clipper.next();
874            }
875        }
876    } else {
877        antifillrect(origR, blitter);
878    }
879}
880
881void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
882                          SkBlitter* blitter) {
883    if (clip.isBW()) {
884        AntiFillRect(r, &clip.bwRgn(), blitter);
885    } else {
886        SkAAClipBlitterWrapper wrap(clip, blitter);
887        AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
888    }
889}
890
891#endif // SK_SCALAR_IS_FLOAT
892
893///////////////////////////////////////////////////////////////////////////////
894
895#define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
896
897// calls blitRect() if the rectangle is non-empty
898static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
899    if (L < R && T < B) {
900        blitter->blitRect(L, T, R - L, B - T);
901    }
902}
903
904static inline FDot8 SkScalarToFDot8(SkScalar x) {
905#ifdef SK_SCALAR_IS_FLOAT
906    return (int)(x * 256);
907#else
908    return x >> 8;
909#endif
910}
911
912static inline int FDot8Floor(FDot8 x) {
913    return x >> 8;
914}
915
916static inline int FDot8Ceil(FDot8 x) {
917    return (x + 0xFF) >> 8;
918}
919
920// 1 - (1 - a)*(1 - b)
921static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
922    // need precise rounding (not just SkAlphaMul) so that values like
923    // a=228, b=252 don't overflow the result
924    return SkToU8(a + b - SkAlphaMulRound(a, b));
925}
926
927static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
928                           SkBlitter* blitter) {
929    SkASSERT(L < R);
930
931    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
932        blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
933        return;
934    }
935
936    int left = L >> 8;
937    if (L & 0xFF) {
938        blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
939        left += 1;
940    }
941
942    int rite = R >> 8;
943    int width = rite - left;
944    if (width > 0) {
945        call_hline_blitter(blitter, left, top, width, alpha);
946    }
947
948    if (R & 0xFF) {
949        blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
950    }
951}
952
953static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
954                            SkBlitter* blitter) {
955    SkASSERT(L < R && T < B);
956
957    int top = T >> 8;
958    if (top == ((B - 1) >> 8)) {   // just one scanline high
959        // We want the inverse of B-T, since we're the inner-stroke
960        int alpha = 256 - (B - T);
961        if (alpha) {
962            inner_scanline(L, top, R, alpha, blitter);
963        }
964        return;
965    }
966
967    if (T & 0xFF) {
968        inner_scanline(L, top, R, T & 0xFF, blitter);
969        top += 1;
970    }
971
972    int bot = B >> 8;
973    int height = bot - top;
974    if (height > 0) {
975        if (L & 0xFF) {
976            blitter->blitV(L >> 8, top, height, L & 0xFF);
977        }
978        if (R & 0xFF) {
979            blitter->blitV(R >> 8, top, height, ~R & 0xFF);
980        }
981    }
982
983    if (B & 0xFF) {
984        inner_scanline(L, bot, R, ~B & 0xFF, blitter);
985    }
986}
987
988void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
989                           const SkRegion* clip, SkBlitter* blitter) {
990    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
991
992    SkScalar rx = SkScalarHalf(strokeSize.fX);
993    SkScalar ry = SkScalarHalf(strokeSize.fY);
994
995    // outset by the radius
996    FDot8 L = SkScalarToFDot8(r.fLeft - rx);
997    FDot8 T = SkScalarToFDot8(r.fTop - ry);
998    FDot8 R = SkScalarToFDot8(r.fRight + rx);
999    FDot8 B = SkScalarToFDot8(r.fBottom + ry);
1000
1001    SkIRect outer;
1002    // set outer to the outer rect of the outer section
1003    outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1004
1005    SkBlitterClipper clipper;
1006    if (clip) {
1007        if (clip->quickReject(outer)) {
1008            return;
1009        }
1010        if (!clip->contains(outer)) {
1011            blitter = clipper.apply(blitter, clip, &outer);
1012        }
1013        // now we can ignore clip for the rest of the function
1014    }
1015
1016    // stroke the outer hull
1017    antifilldot8(L, T, R, B, blitter, false);
1018
1019    // set outer to the outer rect of the middle section
1020    outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
1021
1022    // in case we lost a bit with diameter/2
1023    rx = strokeSize.fX - rx;
1024    ry = strokeSize.fY - ry;
1025    // inset by the radius
1026    L = SkScalarToFDot8(r.fLeft + rx);
1027    T = SkScalarToFDot8(r.fTop + ry);
1028    R = SkScalarToFDot8(r.fRight - rx);
1029    B = SkScalarToFDot8(r.fBottom - ry);
1030
1031    if (L >= R || T >= B) {
1032        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
1033                      blitter);
1034    } else {
1035        SkIRect inner;
1036        // set inner to the inner rect of the middle section
1037        inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
1038
1039        // draw the frame in 4 pieces
1040        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
1041                      blitter);
1042        fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
1043                      blitter);
1044        fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
1045                      blitter);
1046        fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
1047                      blitter);
1048
1049        // now stroke the inner rect, which is similar to antifilldot8() except that
1050        // it treats the fractional coordinates with the inverse bias (since its
1051        // inner).
1052        innerstrokedot8(L, T, R, B, blitter);
1053    }
1054}
1055
1056void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
1057                           const SkRasterClip& clip, SkBlitter* blitter) {
1058    if (clip.isBW()) {
1059        AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
1060    } else {
1061        SkAAClipBlitterWrapper wrap(clip, blitter);
1062        AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
1063    }
1064}
1065
1066