gfx/skia/trunk/src/core/SkScan_Antihair.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/core/SkScan_Antihair.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1054 @@
     1.4 +
     1.5 +/*
     1.6 + * Copyright 2011 The Android Open Source Project
     1.7 + *
     1.8 + * Use of this source code is governed by a BSD-style license that can be
     1.9 + * found in the LICENSE file.
    1.10 + */
    1.11 +
    1.12 +
    1.13 +#include "SkScan.h"
    1.14 +#include "SkBlitter.h"
    1.15 +#include "SkColorPriv.h"
    1.16 +#include "SkLineClipper.h"
    1.17 +#include "SkRasterClip.h"
    1.18 +#include "SkFDot6.h"
    1.19 +
    1.20 +/*  Our attempt to compute the worst case "bounds" for the horizontal and
    1.21 +    vertical cases has some numerical bug in it, and we sometimes undervalue
    1.22 +    our extends. The bug is that when this happens, we will set the clip to
    1.23 +    NULL (for speed), and thus draw outside of the clip by a pixel, which might
    1.24 +    only look bad, but it might also access memory outside of the valid range
    1.25 +    allcoated for the device bitmap.
    1.26 +
    1.27 +    This define enables our fix to outset our "bounds" by 1, thus avoiding the
    1.28 +    chance of the bug, but at the cost of sometimes taking the rectblitter
    1.29 +    case (i.e. not setting the clip to NULL) when we might not actually need
    1.30 +    to. If we can improve/fix the actual calculations, then we can remove this
    1.31 +    step.
    1.32 + */
    1.33 +#define OUTSET_BEFORE_CLIP_TEST     true
    1.34 +
    1.35 +#define HLINE_STACK_BUFFER      100
    1.36 +
    1.37 +static inline int SmallDot6Scale(int value, int dot6) {
    1.38 +    SkASSERT((int16_t)value == value);
    1.39 +    SkASSERT((unsigned)dot6 <= 64);
    1.40 +    return SkMulS16(value, dot6) >> 6;
    1.41 +}
    1.42 +
    1.43 +//#define TEST_GAMMA
    1.44 +
    1.45 +#ifdef TEST_GAMMA
    1.46 +    static uint8_t gGammaTable[256];
    1.47 +    #define ApplyGamma(table, alpha)    (table)[alpha]
    1.48 +
    1.49 +    static void build_gamma_table() {
    1.50 +        static bool gInit = false;
    1.51 +
    1.52 +        if (gInit == false) {
    1.53 +            for (int i = 0; i < 256; i++) {
    1.54 +                SkFixed n = i * 257;
    1.55 +                n += n >> 15;
    1.56 +                SkASSERT(n >= 0 && n <= SK_Fixed1);
    1.57 +                n = SkFixedSqrt(n);
    1.58 +                n = n * 255 >> 16;
    1.59 +            //  SkDebugf("morph %d -> %d\n", i, n);
    1.60 +                gGammaTable[i] = SkToU8(n);
    1.61 +            }
    1.62 +            gInit = true;
    1.63 +        }
    1.64 +    }
    1.65 +#else
    1.66 +    #define ApplyGamma(table, alpha)    SkToU8(alpha)
    1.67 +#endif
    1.68 +
    1.69 +///////////////////////////////////////////////////////////////////////////////
    1.70 +
    1.71 +static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
    1.72 +                               U8CPU alpha) {
    1.73 +    SkASSERT(count > 0);
    1.74 +
    1.75 +    int16_t runs[HLINE_STACK_BUFFER + 1];
    1.76 +    uint8_t  aa[HLINE_STACK_BUFFER];
    1.77 +
    1.78 +    aa[0] = ApplyGamma(gGammaTable, alpha);
    1.79 +    do {
    1.80 +        int n = count;
    1.81 +        if (n > HLINE_STACK_BUFFER) {
    1.82 +            n = HLINE_STACK_BUFFER;
    1.83 +        }
    1.84 +        runs[0] = SkToS16(n);
    1.85 +        runs[n] = 0;
    1.86 +        blitter->blitAntiH(x, y, aa, runs);
    1.87 +        x += n;
    1.88 +        count -= n;
    1.89 +    } while (count > 0);
    1.90 +}
    1.91 +
    1.92 +class SkAntiHairBlitter {
    1.93 +public:
    1.94 +    SkAntiHairBlitter() : fBlitter(NULL) {}
    1.95 +    virtual ~SkAntiHairBlitter() {}
    1.96 +
    1.97 +    SkBlitter* getBlitter() const { return fBlitter; }
    1.98 +
    1.99 +    void setup(SkBlitter* blitter) {
   1.100 +        fBlitter = blitter;
   1.101 +    }
   1.102 +
   1.103 +    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
   1.104 +    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
   1.105 +
   1.106 +private:
   1.107 +    SkBlitter*  fBlitter;
   1.108 +};
   1.109 +
   1.110 +class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
   1.111 +public:
   1.112 +    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
   1.113 +        fy += SK_Fixed1/2;
   1.114 +
   1.115 +        int y = fy >> 16;
   1.116 +        uint8_t  a = (uint8_t)(fy >> 8);
   1.117 +
   1.118 +        // lower line
   1.119 +        unsigned ma = SmallDot6Scale(a, mod64);
   1.120 +        if (ma) {
   1.121 +            call_hline_blitter(this->getBlitter(), x, y, 1, ma);
   1.122 +        }
   1.123 +
   1.124 +        // upper line
   1.125 +        ma = SmallDot6Scale(255 - a, mod64);
   1.126 +        if (ma) {
   1.127 +            call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
   1.128 +        }
   1.129 +
   1.130 +        return fy - SK_Fixed1/2;
   1.131 +    }
   1.132 +
   1.133 +    virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
   1.134 +                             SkFixed slope) SK_OVERRIDE {
   1.135 +        SkASSERT(x < stopx);
   1.136 +        int count = stopx - x;
   1.137 +        fy += SK_Fixed1/2;
   1.138 +
   1.139 +        int y = fy >> 16;
   1.140 +        uint8_t  a = (uint8_t)(fy >> 8);
   1.141 +
   1.142 +        // lower line
   1.143 +        if (a) {
   1.144 +            call_hline_blitter(this->getBlitter(), x, y, count, a);
   1.145 +        }
   1.146 +
   1.147 +        // upper line
   1.148 +        a = 255 - a;
   1.149 +        if (a) {
   1.150 +            call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
   1.151 +        }
   1.152 +
   1.153 +        return fy - SK_Fixed1/2;
   1.154 +    }
   1.155 +};
   1.156 +
   1.157 +class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
   1.158 +public:
   1.159 +    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE {
   1.160 +        int16_t runs[2];
   1.161 +        uint8_t  aa[1];
   1.162 +
   1.163 +        runs[0] = 1;
   1.164 +        runs[1] = 0;
   1.165 +
   1.166 +        fy += SK_Fixed1/2;
   1.167 +        SkBlitter* blitter = this->getBlitter();
   1.168 +
   1.169 +        int lower_y = fy >> 16;
   1.170 +        uint8_t  a = (uint8_t)(fy >> 8);
   1.171 +        unsigned ma = SmallDot6Scale(a, mod64);
   1.172 +        if (ma) {
   1.173 +            aa[0] = ApplyGamma(gamma, ma);
   1.174 +            blitter->blitAntiH(x, lower_y, aa, runs);
   1.175 +            // the clipping blitters might edit runs, but should not affect us
   1.176 +            SkASSERT(runs[0] == 1);
   1.177 +            SkASSERT(runs[1] == 0);
   1.178 +        }
   1.179 +        ma = SmallDot6Scale(255 - a, mod64);
   1.180 +        if (ma) {
   1.181 +            aa[0] = ApplyGamma(gamma, ma);
   1.182 +            blitter->blitAntiH(x, lower_y - 1, aa, runs);
   1.183 +            // the clipping blitters might edit runs, but should not affect us
   1.184 +            SkASSERT(runs[0] == 1);
   1.185 +            SkASSERT(runs[1] == 0);
   1.186 +        }
   1.187 +        fy += dy;
   1.188 +
   1.189 +        return fy - SK_Fixed1/2;
   1.190 +    }
   1.191 +
   1.192 +    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE {
   1.193 +        SkASSERT(x < stopx);
   1.194 +
   1.195 +        int16_t runs[2];
   1.196 +        uint8_t  aa[1];
   1.197 +
   1.198 +        runs[0] = 1;
   1.199 +        runs[1] = 0;
   1.200 +
   1.201 +        fy += SK_Fixed1/2;
   1.202 +        SkBlitter* blitter = this->getBlitter();
   1.203 +        do {
   1.204 +            int lower_y = fy >> 16;
   1.205 +            uint8_t  a = (uint8_t)(fy >> 8);
   1.206 +            if (a) {
   1.207 +                aa[0] = a;
   1.208 +                blitter->blitAntiH(x, lower_y, aa, runs);
   1.209 +                // the clipping blitters might edit runs, but should not affect us
   1.210 +                SkASSERT(runs[0] == 1);
   1.211 +                SkASSERT(runs[1] == 0);
   1.212 +            }
   1.213 +            a = 255 - a;
   1.214 +            if (a) {
   1.215 +                aa[0] = a;
   1.216 +                blitter->blitAntiH(x, lower_y - 1, aa, runs);
   1.217 +                // the clipping blitters might edit runs, but should not affect us
   1.218 +                SkASSERT(runs[0] == 1);
   1.219 +                SkASSERT(runs[1] == 0);
   1.220 +            }
   1.221 +            fy += dy;
   1.222 +        } while (++x < stopx);
   1.223 +
   1.224 +        return fy - SK_Fixed1/2;
   1.225 +    }
   1.226 +};
   1.227 +
   1.228 +class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
   1.229 +public:
   1.230 +    virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
   1.231 +        SkASSERT(0 == dx);
   1.232 +        fx += SK_Fixed1/2;
   1.233 +
   1.234 +        int x = fx >> 16;
   1.235 +        int a = (uint8_t)(fx >> 8);
   1.236 +
   1.237 +        unsigned ma = SmallDot6Scale(a, mod64);
   1.238 +        if (ma) {
   1.239 +            this->getBlitter()->blitV(x, y, 1, ma);
   1.240 +        }
   1.241 +        ma = SmallDot6Scale(255 - a, mod64);
   1.242 +        if (ma) {
   1.243 +            this->getBlitter()->blitV(x - 1, y, 1, ma);
   1.244 +        }
   1.245 +
   1.246 +        return fx - SK_Fixed1/2;
   1.247 +    }
   1.248 +
   1.249 +    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
   1.250 +        SkASSERT(y < stopy);
   1.251 +        SkASSERT(0 == dx);
   1.252 +        fx += SK_Fixed1/2;
   1.253 +
   1.254 +        int x = fx >> 16;
   1.255 +        int a = (uint8_t)(fx >> 8);
   1.256 +
   1.257 +        if (a) {
   1.258 +            this->getBlitter()->blitV(x, y, stopy - y, a);
   1.259 +        }
   1.260 +        a = 255 - a;
   1.261 +        if (a) {
   1.262 +            this->getBlitter()->blitV(x - 1, y, stopy - y, a);
   1.263 +        }
   1.264 +
   1.265 +        return fx - SK_Fixed1/2;
   1.266 +    }
   1.267 +};
   1.268 +
   1.269 +class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
   1.270 +public:
   1.271 +    virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
   1.272 +        int16_t runs[3];
   1.273 +        uint8_t  aa[2];
   1.274 +
   1.275 +        runs[0] = 1;
   1.276 +        runs[2] = 0;
   1.277 +
   1.278 +        fx += SK_Fixed1/2;
   1.279 +        int x = fx >> 16;
   1.280 +        uint8_t  a = (uint8_t)(fx >> 8);
   1.281 +
   1.282 +        aa[0] = SmallDot6Scale(255 - a, mod64);
   1.283 +        aa[1] = SmallDot6Scale(a, mod64);
   1.284 +        // the clippng blitters might overwrite this guy, so we have to reset it each time
   1.285 +        runs[1] = 1;
   1.286 +        this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
   1.287 +        // the clipping blitters might edit runs, but should not affect us
   1.288 +        SkASSERT(runs[0] == 1);
   1.289 +        SkASSERT(runs[2] == 0);
   1.290 +        fx += dx;
   1.291 +
   1.292 +        return fx - SK_Fixed1/2;
   1.293 +    }
   1.294 +
   1.295 +    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
   1.296 +        SkASSERT(y < stopy);
   1.297 +        int16_t runs[3];
   1.298 +        uint8_t  aa[2];
   1.299 +
   1.300 +        runs[0] = 1;
   1.301 +        runs[2] = 0;
   1.302 +
   1.303 +        fx += SK_Fixed1/2;
   1.304 +        do {
   1.305 +            int x = fx >> 16;
   1.306 +            uint8_t  a = (uint8_t)(fx >> 8);
   1.307 +
   1.308 +            aa[0] = 255 - a;
   1.309 +            aa[1] = a;
   1.310 +            // the clippng blitters might overwrite this guy, so we have to reset it each time
   1.311 +            runs[1] = 1;
   1.312 +            this->getBlitter()->blitAntiH(x - 1, y, aa, runs);
   1.313 +            // the clipping blitters might edit runs, but should not affect us
   1.314 +            SkASSERT(runs[0] == 1);
   1.315 +            SkASSERT(runs[2] == 0);
   1.316 +            fx += dx;
   1.317 +        } while (++y < stopy);
   1.318 +
   1.319 +        return fx - SK_Fixed1/2;
   1.320 +    }
   1.321 +};
   1.322 +
   1.323 +static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
   1.324 +    SkASSERT((a << 16 >> 16) == a);
   1.325 +    SkASSERT(b != 0);
   1.326 +    return (a << 16) / b;
   1.327 +}
   1.328 +
   1.329 +#define SkBITCOUNT(x)   (sizeof(x) << 3)
   1.330 +
   1.331 +#if 1
   1.332 +// returns high-bit set iff x==0x8000...
   1.333 +static inline int bad_int(int x) {
   1.334 +    return x & -x;
   1.335 +}
   1.336 +
   1.337 +static int any_bad_ints(int a, int b, int c, int d) {
   1.338 +    return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
   1.339 +}
   1.340 +#else
   1.341 +static inline int good_int(int x) {
   1.342 +    return x ^ (1 << (SkBITCOUNT(x) - 1));
   1.343 +}
   1.344 +
   1.345 +static int any_bad_ints(int a, int b, int c, int d) {
   1.346 +    return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
   1.347 +}
   1.348 +#endif
   1.349 +
   1.350 +#ifdef SK_DEBUG
   1.351 +static bool canConvertFDot6ToFixed(SkFDot6 x) {
   1.352 +    const int maxDot6 = SK_MaxS32 >> (16 - 6);
   1.353 +    return SkAbs32(x) <= maxDot6;
   1.354 +}
   1.355 +#endif
   1.356 +
   1.357 +/*
   1.358 + *  We want the fractional part of ordinate, but we want multiples of 64 to
   1.359 + *  return 64, not 0, so we can't just say (ordinate & 63).
   1.360 + *  We basically want to compute those bits, and if they're 0, return 64.
   1.361 + *  We can do that w/o a branch with an extra sub and add.
   1.362 + */
   1.363 +static int contribution_64(SkFDot6 ordinate) {
   1.364 +#if 0
   1.365 +    int result = ordinate & 63;
   1.366 +    if (0 == result) {
   1.367 +        result = 64;
   1.368 +    }
   1.369 +#else
   1.370 +    int result = ((ordinate - 1) & 63) + 1;
   1.371 +#endif
   1.372 +    SkASSERT(result > 0 && result <= 64);
   1.373 +    return result;
   1.374 +}
   1.375 +
   1.376 +static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
   1.377 +                             const SkIRect* clip, SkBlitter* blitter) {
   1.378 +    // check for integer NaN (0x80000000) which we can't handle (can't negate it)
   1.379 +    // It appears typically from a huge float (inf or nan) being converted to int.
   1.380 +    // If we see it, just don't draw.
   1.381 +    if (any_bad_ints(x0, y0, x1, y1)) {
   1.382 +        return;
   1.383 +    }
   1.384 +
   1.385 +    // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
   1.386 +    // (in dot6 format)
   1.387 +    SkASSERT(canConvertFDot6ToFixed(x0));
   1.388 +    SkASSERT(canConvertFDot6ToFixed(y0));
   1.389 +    SkASSERT(canConvertFDot6ToFixed(x1));
   1.390 +    SkASSERT(canConvertFDot6ToFixed(y1));
   1.391 +
   1.392 +    if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
   1.393 +        /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
   1.394 +            precise, but avoids overflowing the intermediate result if the
   1.395 +            values are huge. A better fix might be to clip the original pts
   1.396 +            directly (i.e. do the divide), so we don't spend time subdividing
   1.397 +            huge lines at all.
   1.398 +         */
   1.399 +        int hx = (x0 >> 1) + (x1 >> 1);
   1.400 +        int hy = (y0 >> 1) + (y1 >> 1);
   1.401 +        do_anti_hairline(x0, y0, hx, hy, clip, blitter);
   1.402 +        do_anti_hairline(hx, hy, x1, y1, clip, blitter);
   1.403 +        return;
   1.404 +    }
   1.405 +
   1.406 +    int         scaleStart, scaleStop;
   1.407 +    int         istart, istop;
   1.408 +    SkFixed     fstart, slope;
   1.409 +
   1.410 +    HLine_SkAntiHairBlitter     hline_blitter;
   1.411 +    Horish_SkAntiHairBlitter    horish_blitter;
   1.412 +    VLine_SkAntiHairBlitter     vline_blitter;
   1.413 +    Vertish_SkAntiHairBlitter   vertish_blitter;
   1.414 +    SkAntiHairBlitter*          hairBlitter = NULL;
   1.415 +
   1.416 +    if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
   1.417 +        if (x0 > x1) {    // we want to go left-to-right
   1.418 +            SkTSwap<SkFDot6>(x0, x1);
   1.419 +            SkTSwap<SkFDot6>(y0, y1);
   1.420 +        }
   1.421 +
   1.422 +        istart = SkFDot6Floor(x0);
   1.423 +        istop = SkFDot6Ceil(x1);
   1.424 +        fstart = SkFDot6ToFixed(y0);
   1.425 +        if (y0 == y1) {   // completely horizontal, take fast case
   1.426 +            slope = 0;
   1.427 +            hairBlitter = &hline_blitter;
   1.428 +        } else {
   1.429 +            slope = fastfixdiv(y1 - y0, x1 - x0);
   1.430 +            SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
   1.431 +            fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
   1.432 +            hairBlitter = &horish_blitter;
   1.433 +        }
   1.434 +
   1.435 +        SkASSERT(istop > istart);
   1.436 +        if (istop - istart == 1) {
   1.437 +            // we are within a single pixel
   1.438 +            scaleStart = x1 - x0;
   1.439 +            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
   1.440 +            scaleStop = 0;
   1.441 +        } else {
   1.442 +            scaleStart = 64 - (x0 & 63);
   1.443 +            scaleStop = x1 & 63;
   1.444 +        }
   1.445 +
   1.446 +        if (clip){
   1.447 +            if (istart >= clip->fRight || istop <= clip->fLeft) {
   1.448 +                return;
   1.449 +            }
   1.450 +            if (istart < clip->fLeft) {
   1.451 +                fstart += slope * (clip->fLeft - istart);
   1.452 +                istart = clip->fLeft;
   1.453 +                scaleStart = 64;
   1.454 +                if (istop - istart == 1) {
   1.455 +                    // we are within a single pixel
   1.456 +                    scaleStart = contribution_64(x1);
   1.457 +                    scaleStop = 0;
   1.458 +                }
   1.459 +            }
   1.460 +            if (istop > clip->fRight) {
   1.461 +                istop = clip->fRight;
   1.462 +                scaleStop = 0;  // so we don't draw this last column
   1.463 +            }
   1.464 +
   1.465 +            SkASSERT(istart <= istop);
   1.466 +            if (istart == istop) {
   1.467 +                return;
   1.468 +            }
   1.469 +            // now test if our Y values are completely inside the clip
   1.470 +            int top, bottom;
   1.471 +            if (slope >= 0) { // T2B
   1.472 +                top = SkFixedFloorToInt(fstart - SK_FixedHalf);
   1.473 +                bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
   1.474 +            } else {           // B2T
   1.475 +                bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
   1.476 +                top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
   1.477 +            }
   1.478 +#ifdef OUTSET_BEFORE_CLIP_TEST
   1.479 +            top -= 1;
   1.480 +            bottom += 1;
   1.481 +#endif
   1.482 +            if (top >= clip->fBottom || bottom <= clip->fTop) {
   1.483 +                return;
   1.484 +            }
   1.485 +            if (clip->fTop <= top && clip->fBottom >= bottom) {
   1.486 +                clip = NULL;
   1.487 +            }
   1.488 +        }
   1.489 +    } else {   // mostly vertical
   1.490 +        if (y0 > y1) {  // we want to go top-to-bottom
   1.491 +            SkTSwap<SkFDot6>(x0, x1);
   1.492 +            SkTSwap<SkFDot6>(y0, y1);
   1.493 +        }
   1.494 +
   1.495 +        istart = SkFDot6Floor(y0);
   1.496 +        istop = SkFDot6Ceil(y1);
   1.497 +        fstart = SkFDot6ToFixed(x0);
   1.498 +        if (x0 == x1) {
   1.499 +            if (y0 == y1) { // are we zero length?
   1.500 +                return;     // nothing to do
   1.501 +            }
   1.502 +            slope = 0;
   1.503 +            hairBlitter = &vline_blitter;
   1.504 +        } else {
   1.505 +            slope = fastfixdiv(x1 - x0, y1 - y0);
   1.506 +            SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
   1.507 +            fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
   1.508 +            hairBlitter = &vertish_blitter;
   1.509 +        }
   1.510 +
   1.511 +        SkASSERT(istop > istart);
   1.512 +        if (istop - istart == 1) {
   1.513 +            // we are within a single pixel
   1.514 +            scaleStart = y1 - y0;
   1.515 +            SkASSERT(scaleStart >= 0 && scaleStart <= 64);
   1.516 +            scaleStop = 0;
   1.517 +        } else {
   1.518 +            scaleStart = 64 - (y0 & 63);
   1.519 +            scaleStop = y1 & 63;
   1.520 +        }
   1.521 +
   1.522 +        if (clip) {
   1.523 +            if (istart >= clip->fBottom || istop <= clip->fTop) {
   1.524 +                return;
   1.525 +            }
   1.526 +            if (istart < clip->fTop) {
   1.527 +                fstart += slope * (clip->fTop - istart);
   1.528 +                istart = clip->fTop;
   1.529 +                scaleStart = 64;
   1.530 +                if (istop - istart == 1) {
   1.531 +                    // we are within a single pixel
   1.532 +                    scaleStart = contribution_64(y1);
   1.533 +                    scaleStop = 0;
   1.534 +                }
   1.535 +            }
   1.536 +            if (istop > clip->fBottom) {
   1.537 +                istop = clip->fBottom;
   1.538 +                scaleStop = 0;  // so we don't draw this last row
   1.539 +            }
   1.540 +
   1.541 +            SkASSERT(istart <= istop);
   1.542 +            if (istart == istop)
   1.543 +                return;
   1.544 +
   1.545 +            // now test if our X values are completely inside the clip
   1.546 +            int left, right;
   1.547 +            if (slope >= 0) { // L2R
   1.548 +                left = SkFixedFloorToInt(fstart - SK_FixedHalf);
   1.549 +                right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
   1.550 +            } else {           // R2L
   1.551 +                right = SkFixedCeilToInt(fstart + SK_FixedHalf);
   1.552 +                left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
   1.553 +            }
   1.554 +#ifdef OUTSET_BEFORE_CLIP_TEST
   1.555 +            left -= 1;
   1.556 +            right += 1;
   1.557 +#endif
   1.558 +            if (left >= clip->fRight || right <= clip->fLeft) {
   1.559 +                return;
   1.560 +            }
   1.561 +            if (clip->fLeft <= left && clip->fRight >= right) {
   1.562 +                clip = NULL;
   1.563 +            }
   1.564 +        }
   1.565 +    }
   1.566 +
   1.567 +    SkRectClipBlitter   rectClipper;
   1.568 +    if (clip) {
   1.569 +        rectClipper.init(blitter, *clip);
   1.570 +        blitter = &rectClipper;
   1.571 +    }
   1.572 +
   1.573 +    SkASSERT(hairBlitter);
   1.574 +    hairBlitter->setup(blitter);
   1.575 +
   1.576 +#ifdef SK_DEBUG
   1.577 +    if (scaleStart > 0 && scaleStop > 0) {
   1.578 +        // be sure we don't draw twice in the same pixel
   1.579 +        SkASSERT(istart < istop - 1);
   1.580 +    }
   1.581 +#endif
   1.582 +
   1.583 +    fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
   1.584 +    istart += 1;
   1.585 +    int fullSpans = istop - istart - (scaleStop > 0);
   1.586 +    if (fullSpans > 0) {
   1.587 +        fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
   1.588 +    }
   1.589 +    if (scaleStop > 0) {
   1.590 +        hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
   1.591 +    }
   1.592 +}
   1.593 +
   1.594 +void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
   1.595 +                             const SkRegion* clip, SkBlitter* blitter) {
   1.596 +    if (clip && clip->isEmpty()) {
   1.597 +        return;
   1.598 +    }
   1.599 +
   1.600 +    SkASSERT(clip == NULL || !clip->getBounds().isEmpty());
   1.601 +
   1.602 +#ifdef TEST_GAMMA
   1.603 +    build_gamma_table();
   1.604 +#endif
   1.605 +
   1.606 +    SkPoint pts[2] = { pt0, pt1 };
   1.607 +
   1.608 +    // We have to pre-clip the line to fit in a SkFixed, so we just chop
   1.609 +    // the line. TODO find a way to actually draw beyond that range.
   1.610 +    {
   1.611 +        SkRect fixedBounds;
   1.612 +        const SkScalar max = SkIntToScalar(32767);
   1.613 +        fixedBounds.set(-max, -max, max, max);
   1.614 +        if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) {
   1.615 +            return;
   1.616 +        }
   1.617 +    }
   1.618 +
   1.619 +    if (clip) {
   1.620 +        SkRect clipBounds;
   1.621 +        clipBounds.set(clip->getBounds());
   1.622 +        /*  We perform integral clipping later on, but we do a scalar clip first
   1.623 +            to ensure that our coordinates are expressible in fixed/integers.
   1.624 +
   1.625 +            antialiased hairlines can draw up to 1/2 of a pixel outside of
   1.626 +            their bounds, so we need to outset the clip before calling the
   1.627 +            clipper. To make the numerics safer, we outset by a whole pixel,
   1.628 +            since the 1/2 pixel boundary is important to the antihair blitter,
   1.629 +            we don't want to risk numerical fate by chopping on that edge.
   1.630 +         */
   1.631 +        clipBounds.inset(-SK_Scalar1, -SK_Scalar1);
   1.632 +
   1.633 +        if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
   1.634 +            return;
   1.635 +        }
   1.636 +    }
   1.637 +
   1.638 +    SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
   1.639 +    SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
   1.640 +    SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
   1.641 +    SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
   1.642 +
   1.643 +    if (clip) {
   1.644 +        SkFDot6 left = SkMin32(x0, x1);
   1.645 +        SkFDot6 top = SkMin32(y0, y1);
   1.646 +        SkFDot6 right = SkMax32(x0, x1);
   1.647 +        SkFDot6 bottom = SkMax32(y0, y1);
   1.648 +        SkIRect ir;
   1.649 +
   1.650 +        ir.set( SkFDot6Floor(left) - 1,
   1.651 +                SkFDot6Floor(top) - 1,
   1.652 +                SkFDot6Ceil(right) + 1,
   1.653 +                SkFDot6Ceil(bottom) + 1);
   1.654 +
   1.655 +        if (clip->quickReject(ir)) {
   1.656 +            return;
   1.657 +        }
   1.658 +        if (!clip->quickContains(ir)) {
   1.659 +            SkRegion::Cliperator iter(*clip, ir);
   1.660 +            const SkIRect*       r = &iter.rect();
   1.661 +
   1.662 +            while (!iter.done()) {
   1.663 +                do_anti_hairline(x0, y0, x1, y1, r, blitter);
   1.664 +                iter.next();
   1.665 +            }
   1.666 +            return;
   1.667 +        }
   1.668 +        // fall through to no-clip case
   1.669 +    }
   1.670 +    do_anti_hairline(x0, y0, x1, y1, NULL, blitter);
   1.671 +}
   1.672 +
   1.673 +void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
   1.674 +                          SkBlitter* blitter) {
   1.675 +    SkPoint p0, p1;
   1.676 +
   1.677 +    p0.set(rect.fLeft, rect.fTop);
   1.678 +    p1.set(rect.fRight, rect.fTop);
   1.679 +    SkScan::AntiHairLine(p0, p1, clip, blitter);
   1.680 +    p0.set(rect.fRight, rect.fBottom);
   1.681 +    SkScan::AntiHairLine(p0, p1, clip, blitter);
   1.682 +    p1.set(rect.fLeft, rect.fBottom);
   1.683 +    SkScan::AntiHairLine(p0, p1, clip, blitter);
   1.684 +    p0.set(rect.fLeft, rect.fTop);
   1.685 +    SkScan::AntiHairLine(p0, p1, clip, blitter);
   1.686 +}
   1.687 +
   1.688 +///////////////////////////////////////////////////////////////////////////////
   1.689 +
   1.690 +typedef int FDot8;  // 24.8 integer fixed point
   1.691 +
   1.692 +static inline FDot8 SkFixedToFDot8(SkFixed x) {
   1.693 +    return (x + 0x80) >> 8;
   1.694 +}
   1.695 +
   1.696 +static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
   1.697 +                        SkBlitter* blitter) {
   1.698 +    SkASSERT(L < R);
   1.699 +
   1.700 +    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
   1.701 +        blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
   1.702 +        return;
   1.703 +    }
   1.704 +
   1.705 +    int left = L >> 8;
   1.706 +
   1.707 +    if (L & 0xFF) {
   1.708 +        blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
   1.709 +        left += 1;
   1.710 +    }
   1.711 +
   1.712 +    int rite = R >> 8;
   1.713 +    int width = rite - left;
   1.714 +    if (width > 0) {
   1.715 +        call_hline_blitter(blitter, left, top, width, alpha);
   1.716 +    }
   1.717 +    if (R & 0xFF) {
   1.718 +        blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
   1.719 +    }
   1.720 +}
   1.721 +
   1.722 +static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
   1.723 +                         bool fillInner) {
   1.724 +    // check for empty now that we're in our reduced precision space
   1.725 +    if (L >= R || T >= B) {
   1.726 +        return;
   1.727 +    }
   1.728 +    int top = T >> 8;
   1.729 +    if (top == ((B - 1) >> 8)) {   // just one scanline high
   1.730 +        do_scanline(L, top, R, B - T - 1, blitter);
   1.731 +        return;
   1.732 +    }
   1.733 +
   1.734 +    if (T & 0xFF) {
   1.735 +        do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
   1.736 +        top += 1;
   1.737 +    }
   1.738 +
   1.739 +    int bot = B >> 8;
   1.740 +    int height = bot - top;
   1.741 +    if (height > 0) {
   1.742 +        int left = L >> 8;
   1.743 +        if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
   1.744 +            blitter->blitV(left, top, height, R - L - 1);
   1.745 +        } else {
   1.746 +            if (L & 0xFF) {
   1.747 +                blitter->blitV(left, top, height, 256 - (L & 0xFF));
   1.748 +                left += 1;
   1.749 +            }
   1.750 +            int rite = R >> 8;
   1.751 +            int width = rite - left;
   1.752 +            if (width > 0 && fillInner) {
   1.753 +                blitter->blitRect(left, top, width, height);
   1.754 +            }
   1.755 +            if (R & 0xFF) {
   1.756 +                blitter->blitV(rite, top, height, R & 0xFF);
   1.757 +            }
   1.758 +        }
   1.759 +    }
   1.760 +
   1.761 +    if (B & 0xFF) {
   1.762 +        do_scanline(L, bot, R, B & 0xFF, blitter);
   1.763 +    }
   1.764 +}
   1.765 +
   1.766 +static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
   1.767 +    antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
   1.768 +                 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
   1.769 +                 blitter, true);
   1.770 +}
   1.771 +
   1.772 +///////////////////////////////////////////////////////////////////////////////
   1.773 +
   1.774 +void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
   1.775 +                          SkBlitter* blitter) {
   1.776 +    if (NULL == clip) {
   1.777 +        antifillrect(xr, blitter);
   1.778 +    } else {
   1.779 +        SkIRect outerBounds;
   1.780 +        XRect_roundOut(xr, &outerBounds);
   1.781 +
   1.782 +        if (clip->isRect()) {
   1.783 +            const SkIRect& clipBounds = clip->getBounds();
   1.784 +
   1.785 +            if (clipBounds.contains(outerBounds)) {
   1.786 +                antifillrect(xr, blitter);
   1.787 +            } else {
   1.788 +                SkXRect tmpR;
   1.789 +                // this keeps our original edges fractional
   1.790 +                XRect_set(&tmpR, clipBounds);
   1.791 +                if (tmpR.intersect(xr)) {
   1.792 +                    antifillrect(tmpR, blitter);
   1.793 +                }
   1.794 +            }
   1.795 +        } else {
   1.796 +            SkRegion::Cliperator clipper(*clip, outerBounds);
   1.797 +            const SkIRect&       rr = clipper.rect();
   1.798 +
   1.799 +            while (!clipper.done()) {
   1.800 +                SkXRect  tmpR;
   1.801 +
   1.802 +                // this keeps our original edges fractional
   1.803 +                XRect_set(&tmpR, rr);
   1.804 +                if (tmpR.intersect(xr)) {
   1.805 +                    antifillrect(tmpR, blitter);
   1.806 +                }
   1.807 +                clipper.next();
   1.808 +            }
   1.809 +        }
   1.810 +    }
   1.811 +}
   1.812 +
   1.813 +void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
   1.814 +                           SkBlitter* blitter) {
   1.815 +    if (clip.isBW()) {
   1.816 +        AntiFillXRect(xr, &clip.bwRgn(), blitter);
   1.817 +    } else {
   1.818 +        SkIRect outerBounds;
   1.819 +        XRect_roundOut(xr, &outerBounds);
   1.820 +
   1.821 +        if (clip.quickContains(outerBounds)) {
   1.822 +            AntiFillXRect(xr, NULL, blitter);
   1.823 +        } else {
   1.824 +            SkAAClipBlitterWrapper wrapper(clip, blitter);
   1.825 +            blitter = wrapper.getBlitter();
   1.826 +
   1.827 +            AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
   1.828 +        }
   1.829 +    }
   1.830 +}
   1.831 +
   1.832 +/*  This guy takes a float-rect, but with the key improvement that it has
   1.833 +    already been clipped, so we know that it is safe to convert it into a
   1.834 +    XRect (fixedpoint), as it won't overflow.
   1.835 +*/
   1.836 +static void antifillrect(const SkRect& r, SkBlitter* blitter) {
   1.837 +    SkXRect xr;
   1.838 +
   1.839 +    XRect_set(&xr, r);
   1.840 +    antifillrect(xr, blitter);
   1.841 +}
   1.842 +
   1.843 +/*  We repeat the clipping logic of AntiFillXRect because the float rect might
   1.844 +    overflow if we blindly converted it to an XRect. This sucks that we have to
   1.845 +    repeat the clipping logic, but I don't see how to share the code/logic.
   1.846 +
   1.847 +    We clip r (as needed) into one or more (smaller) float rects, and then pass
   1.848 +    those to our version of antifillrect, which converts it into an XRect and
   1.849 +    then calls the blit.
   1.850 +*/
   1.851 +void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
   1.852 +                          SkBlitter* blitter) {
   1.853 +    if (clip) {
   1.854 +        SkRect newR;
   1.855 +        newR.set(clip->getBounds());
   1.856 +        if (!newR.intersect(origR)) {
   1.857 +            return;
   1.858 +        }
   1.859 +
   1.860 +        SkIRect outerBounds;
   1.861 +        newR.roundOut(&outerBounds);
   1.862 +
   1.863 +        if (clip->isRect()) {
   1.864 +            antifillrect(newR, blitter);
   1.865 +        } else {
   1.866 +            SkRegion::Cliperator clipper(*clip, outerBounds);
   1.867 +            while (!clipper.done()) {
   1.868 +                newR.set(clipper.rect());
   1.869 +                if (newR.intersect(origR)) {
   1.870 +                    antifillrect(newR, blitter);
   1.871 +                }
   1.872 +                clipper.next();
   1.873 +            }
   1.874 +        }
   1.875 +    } else {
   1.876 +        antifillrect(origR, blitter);
   1.877 +    }
   1.878 +}
   1.879 +
   1.880 +void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
   1.881 +                          SkBlitter* blitter) {
   1.882 +    if (clip.isBW()) {
   1.883 +        AntiFillRect(r, &clip.bwRgn(), blitter);
   1.884 +    } else {
   1.885 +        SkAAClipBlitterWrapper wrap(clip, blitter);
   1.886 +        AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
   1.887 +    }
   1.888 +}
   1.889 +
   1.890 +///////////////////////////////////////////////////////////////////////////////
   1.891 +
   1.892 +#define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
   1.893 +
   1.894 +// calls blitRect() if the rectangle is non-empty
   1.895 +static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
   1.896 +    if (L < R && T < B) {
   1.897 +        blitter->blitRect(L, T, R - L, B - T);
   1.898 +    }
   1.899 +}
   1.900 +
   1.901 +static inline FDot8 SkScalarToFDot8(SkScalar x) {
   1.902 +    return (int)(x * 256);
   1.903 +}
   1.904 +
   1.905 +static inline int FDot8Floor(FDot8 x) {
   1.906 +    return x >> 8;
   1.907 +}
   1.908 +
   1.909 +static inline int FDot8Ceil(FDot8 x) {
   1.910 +    return (x + 0xFF) >> 8;
   1.911 +}
   1.912 +
   1.913 +// 1 - (1 - a)*(1 - b)
   1.914 +static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
   1.915 +    // need precise rounding (not just SkAlphaMul) so that values like
   1.916 +    // a=228, b=252 don't overflow the result
   1.917 +    return SkToU8(a + b - SkAlphaMulRound(a, b));
   1.918 +}
   1.919 +
   1.920 +static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
   1.921 +                           SkBlitter* blitter) {
   1.922 +    SkASSERT(L < R);
   1.923 +
   1.924 +    if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
   1.925 +        blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
   1.926 +        return;
   1.927 +    }
   1.928 +
   1.929 +    int left = L >> 8;
   1.930 +    if (L & 0xFF) {
   1.931 +        blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
   1.932 +        left += 1;
   1.933 +    }
   1.934 +
   1.935 +    int rite = R >> 8;
   1.936 +    int width = rite - left;
   1.937 +    if (width > 0) {
   1.938 +        call_hline_blitter(blitter, left, top, width, alpha);
   1.939 +    }
   1.940 +
   1.941 +    if (R & 0xFF) {
   1.942 +        blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
   1.943 +    }
   1.944 +}
   1.945 +
   1.946 +static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
   1.947 +                            SkBlitter* blitter) {
   1.948 +    SkASSERT(L < R && T < B);
   1.949 +
   1.950 +    int top = T >> 8;
   1.951 +    if (top == ((B - 1) >> 8)) {   // just one scanline high
   1.952 +        // We want the inverse of B-T, since we're the inner-stroke
   1.953 +        int alpha = 256 - (B - T);
   1.954 +        if (alpha) {
   1.955 +            inner_scanline(L, top, R, alpha, blitter);
   1.956 +        }
   1.957 +        return;
   1.958 +    }
   1.959 +
   1.960 +    if (T & 0xFF) {
   1.961 +        inner_scanline(L, top, R, T & 0xFF, blitter);
   1.962 +        top += 1;
   1.963 +    }
   1.964 +
   1.965 +    int bot = B >> 8;
   1.966 +    int height = bot - top;
   1.967 +    if (height > 0) {
   1.968 +        if (L & 0xFF) {
   1.969 +            blitter->blitV(L >> 8, top, height, L & 0xFF);
   1.970 +        }
   1.971 +        if (R & 0xFF) {
   1.972 +            blitter->blitV(R >> 8, top, height, ~R & 0xFF);
   1.973 +        }
   1.974 +    }
   1.975 +
   1.976 +    if (B & 0xFF) {
   1.977 +        inner_scanline(L, bot, R, ~B & 0xFF, blitter);
   1.978 +    }
   1.979 +}
   1.980 +
   1.981 +void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
   1.982 +                           const SkRegion* clip, SkBlitter* blitter) {
   1.983 +    SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
   1.984 +
   1.985 +    SkScalar rx = SkScalarHalf(strokeSize.fX);
   1.986 +    SkScalar ry = SkScalarHalf(strokeSize.fY);
   1.987 +
   1.988 +    // outset by the radius
   1.989 +    FDot8 L = SkScalarToFDot8(r.fLeft - rx);
   1.990 +    FDot8 T = SkScalarToFDot8(r.fTop - ry);
   1.991 +    FDot8 R = SkScalarToFDot8(r.fRight + rx);
   1.992 +    FDot8 B = SkScalarToFDot8(r.fBottom + ry);
   1.993 +
   1.994 +    SkIRect outer;
   1.995 +    // set outer to the outer rect of the outer section
   1.996 +    outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
   1.997 +
   1.998 +    SkBlitterClipper clipper;
   1.999 +    if (clip) {
  1.1000 +        if (clip->quickReject(outer)) {
  1.1001 +            return;
  1.1002 +        }
  1.1003 +        if (!clip->contains(outer)) {
  1.1004 +            blitter = clipper.apply(blitter, clip, &outer);
  1.1005 +        }
  1.1006 +        // now we can ignore clip for the rest of the function
  1.1007 +    }
  1.1008 +
  1.1009 +    // stroke the outer hull
  1.1010 +    antifilldot8(L, T, R, B, blitter, false);
  1.1011 +
  1.1012 +    // set outer to the outer rect of the middle section
  1.1013 +    outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B));
  1.1014 +
  1.1015 +    // in case we lost a bit with diameter/2
  1.1016 +    rx = strokeSize.fX - rx;
  1.1017 +    ry = strokeSize.fY - ry;
  1.1018 +    // inset by the radius
  1.1019 +    L = SkScalarToFDot8(r.fLeft + rx);
  1.1020 +    T = SkScalarToFDot8(r.fTop + ry);
  1.1021 +    R = SkScalarToFDot8(r.fRight - rx);
  1.1022 +    B = SkScalarToFDot8(r.fBottom - ry);
  1.1023 +
  1.1024 +    if (L >= R || T >= B) {
  1.1025 +        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
  1.1026 +                      blitter);
  1.1027 +    } else {
  1.1028 +        SkIRect inner;
  1.1029 +        // set inner to the inner rect of the middle section
  1.1030 +        inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B));
  1.1031 +
  1.1032 +        // draw the frame in 4 pieces
  1.1033 +        fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
  1.1034 +                      blitter);
  1.1035 +        fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
  1.1036 +                      blitter);
  1.1037 +        fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
  1.1038 +                      blitter);
  1.1039 +        fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
  1.1040 +                      blitter);
  1.1041 +
  1.1042 +        // now stroke the inner rect, which is similar to antifilldot8() except that
  1.1043 +        // it treats the fractional coordinates with the inverse bias (since its
  1.1044 +        // inner).
  1.1045 +        innerstrokedot8(L, T, R, B, blitter);
  1.1046 +    }
  1.1047 +}
  1.1048 +
  1.1049 +void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
  1.1050 +                           const SkRasterClip& clip, SkBlitter* blitter) {
  1.1051 +    if (clip.isBW()) {
  1.1052 +        AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
  1.1053 +    } else {
  1.1054 +        SkAAClipBlitterWrapper wrap(clip, blitter);
  1.1055 +        AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
  1.1056 +    }
  1.1057 +}

mercurial