|  |  | 
|  | /* | 
|  | * Copyright 2011 The Android Open Source Project | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include "SkScan.h" | 
|  | #include "SkBlitter.h" | 
|  | #include "SkColorPriv.h" | 
|  | #include "SkLineClipper.h" | 
|  | #include "SkRasterClip.h" | 
|  | #include "SkFDot6.h" | 
|  |  | 
|  | /*  Our attempt to compute the worst case "bounds" for the horizontal and | 
|  | vertical cases has some numerical bug in it, and we sometimes undervalue | 
|  | our extends. The bug is that when this happens, we will set the clip to | 
|  | NULL (for speed), and thus draw outside of the clip by a pixel, which might | 
|  | only look bad, but it might also access memory outside of the valid range | 
|  | allcoated for the device bitmap. | 
|  |  | 
|  | This define enables our fix to outset our "bounds" by 1, thus avoiding the | 
|  | chance of the bug, but at the cost of sometimes taking the rectblitter | 
|  | case (i.e. not setting the clip to NULL) when we might not actually need | 
|  | to. If we can improve/fix the actual calculations, then we can remove this | 
|  | step. | 
|  | */ | 
|  | #define OUTSET_BEFORE_CLIP_TEST     true | 
|  |  | 
|  | #define HLINE_STACK_BUFFER      100 | 
|  |  | 
|  | static inline int SmallDot6Scale(int value, int dot6) { | 
|  | SkASSERT((int16_t)value == value); | 
|  | SkASSERT((unsigned)dot6 <= 64); | 
|  | return SkMulS16(value, dot6) >> 6; | 
|  | } | 
|  |  | 
|  | //#define TEST_GAMMA | 
|  |  | 
|  | #ifdef TEST_GAMMA | 
|  | static uint8_t gGammaTable[256]; | 
|  | #define ApplyGamma(table, alpha)    (table)[alpha] | 
|  |  | 
|  | static void build_gamma_table() { | 
|  | static bool gInit = false; | 
|  |  | 
|  | if (gInit == false) { | 
|  | for (int i = 0; i < 256; i++) { | 
|  | SkFixed n = i * 257; | 
|  | n += n >> 15; | 
|  | SkASSERT(n >= 0 && n <= SK_Fixed1); | 
|  | n = SkFixedSqrt(n); | 
|  | n = n * 255 >> 16; | 
|  | //  SkDebugf("morph %d -> %d\n", i, n); | 
|  | gGammaTable[i] = SkToU8(n); | 
|  | } | 
|  | gInit = true; | 
|  | } | 
|  | } | 
|  | #else | 
|  | #define ApplyGamma(table, alpha)    SkToU8(alpha) | 
|  | #endif | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, | 
|  | U8CPU alpha) { | 
|  | SkASSERT(count > 0); | 
|  |  | 
|  | int16_t runs[HLINE_STACK_BUFFER + 1]; | 
|  | uint8_t  aa[HLINE_STACK_BUFFER]; | 
|  |  | 
|  | aa[0] = ApplyGamma(gGammaTable, alpha); | 
|  | do { | 
|  | int n = count; | 
|  | if (n > HLINE_STACK_BUFFER) { | 
|  | n = HLINE_STACK_BUFFER; | 
|  | } | 
|  | runs[0] = SkToS16(n); | 
|  | runs[n] = 0; | 
|  | blitter->blitAntiH(x, y, aa, runs); | 
|  | x += n; | 
|  | count -= n; | 
|  | } while (count > 0); | 
|  | } | 
|  |  | 
|  | class SkAntiHairBlitter { | 
|  | public: | 
|  | SkAntiHairBlitter() : fBlitter(NULL) {} | 
|  | virtual ~SkAntiHairBlitter() {} | 
|  |  | 
|  | SkBlitter* getBlitter() const { return fBlitter; } | 
|  |  | 
|  | void setup(SkBlitter* blitter) { | 
|  | fBlitter = blitter; | 
|  | } | 
|  |  | 
|  | virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0; | 
|  | virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0; | 
|  |  | 
|  | private: | 
|  | SkBlitter*  fBlitter; | 
|  | }; | 
|  |  | 
|  | class HLine_SkAntiHairBlitter : public SkAntiHairBlitter { | 
|  | public: | 
|  | virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE { | 
|  | fy += SK_Fixed1/2; | 
|  |  | 
|  | int y = fy >> 16; | 
|  | uint8_t  a = (uint8_t)(fy >> 8); | 
|  |  | 
|  | // lower line | 
|  | unsigned ma = SmallDot6Scale(a, mod64); | 
|  | if (ma) { | 
|  | call_hline_blitter(this->getBlitter(), x, y, 1, ma); | 
|  | } | 
|  |  | 
|  | // upper line | 
|  | ma = SmallDot6Scale(255 - a, mod64); | 
|  | if (ma) { | 
|  | call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma); | 
|  | } | 
|  |  | 
|  | return fy - SK_Fixed1/2; | 
|  | } | 
|  |  | 
|  | virtual SkFixed drawLine(int x, int stopx, SkFixed fy, | 
|  | SkFixed slope) SK_OVERRIDE { | 
|  | SkASSERT(x < stopx); | 
|  | int count = stopx - x; | 
|  | fy += SK_Fixed1/2; | 
|  |  | 
|  | int y = fy >> 16; | 
|  | uint8_t  a = (uint8_t)(fy >> 8); | 
|  |  | 
|  | // lower line | 
|  | if (a) { | 
|  | call_hline_blitter(this->getBlitter(), x, y, count, a); | 
|  | } | 
|  |  | 
|  | // upper line | 
|  | a = 255 - a; | 
|  | if (a) { | 
|  | call_hline_blitter(this->getBlitter(), x, y - 1, count, a); | 
|  | } | 
|  |  | 
|  | return fy - SK_Fixed1/2; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class Horish_SkAntiHairBlitter : public SkAntiHairBlitter { | 
|  | public: | 
|  | virtual SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) SK_OVERRIDE { | 
|  | int16_t runs[2]; | 
|  | uint8_t  aa[1]; | 
|  |  | 
|  | runs[0] = 1; | 
|  | runs[1] = 0; | 
|  |  | 
|  | fy += SK_Fixed1/2; | 
|  | SkBlitter* blitter = this->getBlitter(); | 
|  |  | 
|  | int lower_y = fy >> 16; | 
|  | uint8_t  a = (uint8_t)(fy >> 8); | 
|  | unsigned ma = SmallDot6Scale(a, mod64); | 
|  | if (ma) { | 
|  | aa[0] = ApplyGamma(gamma, ma); | 
|  | blitter->blitAntiH(x, lower_y, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[1] == 0); | 
|  | } | 
|  | ma = SmallDot6Scale(255 - a, mod64); | 
|  | if (ma) { | 
|  | aa[0] = ApplyGamma(gamma, ma); | 
|  | blitter->blitAntiH(x, lower_y - 1, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[1] == 0); | 
|  | } | 
|  | fy += dy; | 
|  |  | 
|  | return fy - SK_Fixed1/2; | 
|  | } | 
|  |  | 
|  | virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_OVERRIDE { | 
|  | SkASSERT(x < stopx); | 
|  |  | 
|  | int16_t runs[2]; | 
|  | uint8_t  aa[1]; | 
|  |  | 
|  | runs[0] = 1; | 
|  | runs[1] = 0; | 
|  |  | 
|  | fy += SK_Fixed1/2; | 
|  | SkBlitter* blitter = this->getBlitter(); | 
|  | do { | 
|  | int lower_y = fy >> 16; | 
|  | uint8_t  a = (uint8_t)(fy >> 8); | 
|  | if (a) { | 
|  | aa[0] = a; | 
|  | blitter->blitAntiH(x, lower_y, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[1] == 0); | 
|  | } | 
|  | a = 255 - a; | 
|  | if (a) { | 
|  | aa[0] = a; | 
|  | blitter->blitAntiH(x, lower_y - 1, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[1] == 0); | 
|  | } | 
|  | fy += dy; | 
|  | } while (++x < stopx); | 
|  |  | 
|  | return fy - SK_Fixed1/2; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class VLine_SkAntiHairBlitter : public SkAntiHairBlitter { | 
|  | public: | 
|  | virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE { | 
|  | SkASSERT(0 == dx); | 
|  | fx += SK_Fixed1/2; | 
|  |  | 
|  | int x = fx >> 16; | 
|  | int a = (uint8_t)(fx >> 8); | 
|  |  | 
|  | unsigned ma = SmallDot6Scale(a, mod64); | 
|  | if (ma) { | 
|  | this->getBlitter()->blitV(x, y, 1, ma); | 
|  | } | 
|  | ma = SmallDot6Scale(255 - a, mod64); | 
|  | if (ma) { | 
|  | this->getBlitter()->blitV(x - 1, y, 1, ma); | 
|  | } | 
|  |  | 
|  | return fx - SK_Fixed1/2; | 
|  | } | 
|  |  | 
|  | virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE { | 
|  | SkASSERT(y < stopy); | 
|  | SkASSERT(0 == dx); | 
|  | fx += SK_Fixed1/2; | 
|  |  | 
|  | int x = fx >> 16; | 
|  | int a = (uint8_t)(fx >> 8); | 
|  |  | 
|  | if (a) { | 
|  | this->getBlitter()->blitV(x, y, stopy - y, a); | 
|  | } | 
|  | a = 255 - a; | 
|  | if (a) { | 
|  | this->getBlitter()->blitV(x - 1, y, stopy - y, a); | 
|  | } | 
|  |  | 
|  | return fx - SK_Fixed1/2; | 
|  | } | 
|  | }; | 
|  |  | 
|  | class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter { | 
|  | public: | 
|  | virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE { | 
|  | int16_t runs[3]; | 
|  | uint8_t  aa[2]; | 
|  |  | 
|  | runs[0] = 1; | 
|  | runs[2] = 0; | 
|  |  | 
|  | fx += SK_Fixed1/2; | 
|  | int x = fx >> 16; | 
|  | uint8_t  a = (uint8_t)(fx >> 8); | 
|  |  | 
|  | aa[0] = SmallDot6Scale(255 - a, mod64); | 
|  | aa[1] = SmallDot6Scale(a, mod64); | 
|  | // the clippng blitters might overwrite this guy, so we have to reset it each time | 
|  | runs[1] = 1; | 
|  | this->getBlitter()->blitAntiH(x - 1, y, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[2] == 0); | 
|  | fx += dx; | 
|  |  | 
|  | return fx - SK_Fixed1/2; | 
|  | } | 
|  |  | 
|  | virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE { | 
|  | SkASSERT(y < stopy); | 
|  | int16_t runs[3]; | 
|  | uint8_t  aa[2]; | 
|  |  | 
|  | runs[0] = 1; | 
|  | runs[2] = 0; | 
|  |  | 
|  | fx += SK_Fixed1/2; | 
|  | do { | 
|  | int x = fx >> 16; | 
|  | uint8_t  a = (uint8_t)(fx >> 8); | 
|  |  | 
|  | aa[0] = 255 - a; | 
|  | aa[1] = a; | 
|  | // the clippng blitters might overwrite this guy, so we have to reset it each time | 
|  | runs[1] = 1; | 
|  | this->getBlitter()->blitAntiH(x - 1, y, aa, runs); | 
|  | // the clipping blitters might edit runs, but should not affect us | 
|  | SkASSERT(runs[0] == 1); | 
|  | SkASSERT(runs[2] == 0); | 
|  | fx += dx; | 
|  | } while (++y < stopy); | 
|  |  | 
|  | return fx - SK_Fixed1/2; | 
|  | } | 
|  | }; | 
|  |  | 
|  | static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) { | 
|  | SkASSERT((a << 16 >> 16) == a); | 
|  | SkASSERT(b != 0); | 
|  | return (a << 16) / b; | 
|  | } | 
|  |  | 
|  | #define SkBITCOUNT(x)   (sizeof(x) << 3) | 
|  |  | 
|  | #if 1 | 
|  | // returns high-bit set iff x==0x8000... | 
|  | static inline int bad_int(int x) { | 
|  | return x & -x; | 
|  | } | 
|  |  | 
|  | static int any_bad_ints(int a, int b, int c, int d) { | 
|  | return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1); | 
|  | } | 
|  | #else | 
|  | static inline int good_int(int x) { | 
|  | return x ^ (1 << (SkBITCOUNT(x) - 1)); | 
|  | } | 
|  |  | 
|  | static int any_bad_ints(int a, int b, int c, int d) { | 
|  | return !(good_int(a) & good_int(b) & good_int(c) & good_int(d)); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | static bool canConvertFDot6ToFixed(SkFDot6 x) { | 
|  | const int maxDot6 = SK_MaxS32 >> (16 - 6); | 
|  | return SkAbs32(x) <= maxDot6; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | /* | 
|  | *  We want the fractional part of ordinate, but we want multiples of 64 to | 
|  | *  return 64, not 0, so we can't just say (ordinate & 63). | 
|  | *  We basically want to compute those bits, and if they're 0, return 64. | 
|  | *  We can do that w/o a branch with an extra sub and add. | 
|  | */ | 
|  | static int contribution_64(SkFDot6 ordinate) { | 
|  | #if 0 | 
|  | int result = ordinate & 63; | 
|  | if (0 == result) { | 
|  | result = 64; | 
|  | } | 
|  | #else | 
|  | int result = ((ordinate - 1) & 63) + 1; | 
|  | #endif | 
|  | SkASSERT(result > 0 && result <= 64); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, | 
|  | const SkIRect* clip, SkBlitter* blitter) { | 
|  | // check for integer NaN (0x80000000) which we can't handle (can't negate it) | 
|  | // It appears typically from a huge float (inf or nan) being converted to int. | 
|  | // If we see it, just don't draw. | 
|  | if (any_bad_ints(x0, y0, x1, y1)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time | 
|  | // (in dot6 format) | 
|  | SkASSERT(canConvertFDot6ToFixed(x0)); | 
|  | SkASSERT(canConvertFDot6ToFixed(y0)); | 
|  | SkASSERT(canConvertFDot6ToFixed(x1)); | 
|  | SkASSERT(canConvertFDot6ToFixed(y1)); | 
|  |  | 
|  | if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) { | 
|  | /*  instead of (x0 + x1) >> 1, we shift each separately. This is less | 
|  | precise, but avoids overflowing the intermediate result if the | 
|  | values are huge. A better fix might be to clip the original pts | 
|  | directly (i.e. do the divide), so we don't spend time subdividing | 
|  | huge lines at all. | 
|  | */ | 
|  | int hx = (x0 >> 1) + (x1 >> 1); | 
|  | int hy = (y0 >> 1) + (y1 >> 1); | 
|  | do_anti_hairline(x0, y0, hx, hy, clip, blitter); | 
|  | do_anti_hairline(hx, hy, x1, y1, clip, blitter); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int         scaleStart, scaleStop; | 
|  | int         istart, istop; | 
|  | SkFixed     fstart, slope; | 
|  |  | 
|  | HLine_SkAntiHairBlitter     hline_blitter; | 
|  | Horish_SkAntiHairBlitter    horish_blitter; | 
|  | VLine_SkAntiHairBlitter     vline_blitter; | 
|  | Vertish_SkAntiHairBlitter   vertish_blitter; | 
|  | SkAntiHairBlitter*          hairBlitter = NULL; | 
|  |  | 
|  | if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal | 
|  | if (x0 > x1) {    // we want to go left-to-right | 
|  | SkTSwap<SkFDot6>(x0, x1); | 
|  | SkTSwap<SkFDot6>(y0, y1); | 
|  | } | 
|  |  | 
|  | istart = SkFDot6Floor(x0); | 
|  | istop = SkFDot6Ceil(x1); | 
|  | fstart = SkFDot6ToFixed(y0); | 
|  | if (y0 == y1) {   // completely horizontal, take fast case | 
|  | slope = 0; | 
|  | hairBlitter = &hline_blitter; | 
|  | } else { | 
|  | slope = fastfixdiv(y1 - y0, x1 - x0); | 
|  | SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); | 
|  | fstart += (slope * (32 - (x0 & 63)) + 32) >> 6; | 
|  | hairBlitter = &horish_blitter; | 
|  | } | 
|  |  | 
|  | SkASSERT(istop > istart); | 
|  | if (istop - istart == 1) { | 
|  | // we are within a single pixel | 
|  | scaleStart = x1 - x0; | 
|  | SkASSERT(scaleStart >= 0 && scaleStart <= 64); | 
|  | scaleStop = 0; | 
|  | } else { | 
|  | scaleStart = 64 - (x0 & 63); | 
|  | scaleStop = x1 & 63; | 
|  | } | 
|  |  | 
|  | if (clip){ | 
|  | if (istart >= clip->fRight || istop <= clip->fLeft) { | 
|  | return; | 
|  | } | 
|  | if (istart < clip->fLeft) { | 
|  | fstart += slope * (clip->fLeft - istart); | 
|  | istart = clip->fLeft; | 
|  | scaleStart = 64; | 
|  | if (istop - istart == 1) { | 
|  | // we are within a single pixel | 
|  | scaleStart = contribution_64(x1); | 
|  | scaleStop = 0; | 
|  | } | 
|  | } | 
|  | if (istop > clip->fRight) { | 
|  | istop = clip->fRight; | 
|  | scaleStop = 0;  // so we don't draw this last column | 
|  | } | 
|  |  | 
|  | SkASSERT(istart <= istop); | 
|  | if (istart == istop) { | 
|  | return; | 
|  | } | 
|  | // now test if our Y values are completely inside the clip | 
|  | int top, bottom; | 
|  | if (slope >= 0) { // T2B | 
|  | top = SkFixedFloorToInt(fstart - SK_FixedHalf); | 
|  | bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf); | 
|  | } else {           // B2T | 
|  | bottom = SkFixedCeilToInt(fstart + SK_FixedHalf); | 
|  | top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf); | 
|  | } | 
|  | #ifdef OUTSET_BEFORE_CLIP_TEST | 
|  | top -= 1; | 
|  | bottom += 1; | 
|  | #endif | 
|  | if (top >= clip->fBottom || bottom <= clip->fTop) { | 
|  | return; | 
|  | } | 
|  | if (clip->fTop <= top && clip->fBottom >= bottom) { | 
|  | clip = NULL; | 
|  | } | 
|  | } | 
|  | } else {   // mostly vertical | 
|  | if (y0 > y1) {  // we want to go top-to-bottom | 
|  | SkTSwap<SkFDot6>(x0, x1); | 
|  | SkTSwap<SkFDot6>(y0, y1); | 
|  | } | 
|  |  | 
|  | istart = SkFDot6Floor(y0); | 
|  | istop = SkFDot6Ceil(y1); | 
|  | fstart = SkFDot6ToFixed(x0); | 
|  | if (x0 == x1) { | 
|  | if (y0 == y1) { // are we zero length? | 
|  | return;     // nothing to do | 
|  | } | 
|  | slope = 0; | 
|  | hairBlitter = &vline_blitter; | 
|  | } else { | 
|  | slope = fastfixdiv(x1 - x0, y1 - y0); | 
|  | SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); | 
|  | fstart += (slope * (32 - (y0 & 63)) + 32) >> 6; | 
|  | hairBlitter = &vertish_blitter; | 
|  | } | 
|  |  | 
|  | SkASSERT(istop > istart); | 
|  | if (istop - istart == 1) { | 
|  | // we are within a single pixel | 
|  | scaleStart = y1 - y0; | 
|  | SkASSERT(scaleStart >= 0 && scaleStart <= 64); | 
|  | scaleStop = 0; | 
|  | } else { | 
|  | scaleStart = 64 - (y0 & 63); | 
|  | scaleStop = y1 & 63; | 
|  | } | 
|  |  | 
|  | if (clip) { | 
|  | if (istart >= clip->fBottom || istop <= clip->fTop) { | 
|  | return; | 
|  | } | 
|  | if (istart < clip->fTop) { | 
|  | fstart += slope * (clip->fTop - istart); | 
|  | istart = clip->fTop; | 
|  | scaleStart = 64; | 
|  | if (istop - istart == 1) { | 
|  | // we are within a single pixel | 
|  | scaleStart = contribution_64(y1); | 
|  | scaleStop = 0; | 
|  | } | 
|  | } | 
|  | if (istop > clip->fBottom) { | 
|  | istop = clip->fBottom; | 
|  | scaleStop = 0;  // so we don't draw this last row | 
|  | } | 
|  |  | 
|  | SkASSERT(istart <= istop); | 
|  | if (istart == istop) | 
|  | return; | 
|  |  | 
|  | // now test if our X values are completely inside the clip | 
|  | int left, right; | 
|  | if (slope >= 0) { // L2R | 
|  | left = SkFixedFloorToInt(fstart - SK_FixedHalf); | 
|  | right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf); | 
|  | } else {           // R2L | 
|  | right = SkFixedCeilToInt(fstart + SK_FixedHalf); | 
|  | left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf); | 
|  | } | 
|  | #ifdef OUTSET_BEFORE_CLIP_TEST | 
|  | left -= 1; | 
|  | right += 1; | 
|  | #endif | 
|  | if (left >= clip->fRight || right <= clip->fLeft) { | 
|  | return; | 
|  | } | 
|  | if (clip->fLeft <= left && clip->fRight >= right) { | 
|  | clip = NULL; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | SkRectClipBlitter   rectClipper; | 
|  | if (clip) { | 
|  | rectClipper.init(blitter, *clip); | 
|  | blitter = &rectClipper; | 
|  | } | 
|  |  | 
|  | SkASSERT(hairBlitter); | 
|  | hairBlitter->setup(blitter); | 
|  |  | 
|  | #ifdef SK_DEBUG | 
|  | if (scaleStart > 0 && scaleStop > 0) { | 
|  | // be sure we don't draw twice in the same pixel | 
|  | SkASSERT(istart < istop - 1); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart); | 
|  | istart += 1; | 
|  | int fullSpans = istop - istart - (scaleStop > 0); | 
|  | if (fullSpans > 0) { | 
|  | fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope); | 
|  | } | 
|  | if (scaleStop > 0) { | 
|  | hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1, | 
|  | const SkRegion* clip, SkBlitter* blitter) { | 
|  | if (clip && clip->isEmpty()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkASSERT(clip == NULL || !clip->getBounds().isEmpty()); | 
|  |  | 
|  | #ifdef TEST_GAMMA | 
|  | build_gamma_table(); | 
|  | #endif | 
|  |  | 
|  | SkPoint pts[2] = { pt0, pt1 }; | 
|  |  | 
|  | // We have to pre-clip the line to fit in a SkFixed, so we just chop | 
|  | // the line. TODO find a way to actually draw beyond that range. | 
|  | { | 
|  | SkRect fixedBounds; | 
|  | const SkScalar max = SkIntToScalar(32767); | 
|  | fixedBounds.set(-max, -max, max, max); | 
|  | if (!SkLineClipper::IntersectLine(pts, fixedBounds, pts)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (clip) { | 
|  | SkRect clipBounds; | 
|  | clipBounds.set(clip->getBounds()); | 
|  | /*  We perform integral clipping later on, but we do a scalar clip first | 
|  | to ensure that our coordinates are expressible in fixed/integers. | 
|  |  | 
|  | antialiased hairlines can draw up to 1/2 of a pixel outside of | 
|  | their bounds, so we need to outset the clip before calling the | 
|  | clipper. To make the numerics safer, we outset by a whole pixel, | 
|  | since the 1/2 pixel boundary is important to the antihair blitter, | 
|  | we don't want to risk numerical fate by chopping on that edge. | 
|  | */ | 
|  | clipBounds.inset(-SK_Scalar1, -SK_Scalar1); | 
|  |  | 
|  | if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) { | 
|  | return; | 
|  | } | 
|  | } | 
|  |  | 
|  | SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); | 
|  | SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); | 
|  | SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); | 
|  | SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); | 
|  |  | 
|  | if (clip) { | 
|  | SkFDot6 left = SkMin32(x0, x1); | 
|  | SkFDot6 top = SkMin32(y0, y1); | 
|  | SkFDot6 right = SkMax32(x0, x1); | 
|  | SkFDot6 bottom = SkMax32(y0, y1); | 
|  | SkIRect ir; | 
|  |  | 
|  | ir.set( SkFDot6Floor(left) - 1, | 
|  | SkFDot6Floor(top) - 1, | 
|  | SkFDot6Ceil(right) + 1, | 
|  | SkFDot6Ceil(bottom) + 1); | 
|  |  | 
|  | if (clip->quickReject(ir)) { | 
|  | return; | 
|  | } | 
|  | if (!clip->quickContains(ir)) { | 
|  | SkRegion::Cliperator iter(*clip, ir); | 
|  | const SkIRect*       r = &iter.rect(); | 
|  |  | 
|  | while (!iter.done()) { | 
|  | do_anti_hairline(x0, y0, x1, y1, r, blitter); | 
|  | iter.next(); | 
|  | } | 
|  | return; | 
|  | } | 
|  | // fall through to no-clip case | 
|  | } | 
|  | do_anti_hairline(x0, y0, x1, y1, NULL, blitter); | 
|  | } | 
|  |  | 
|  | void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip, | 
|  | SkBlitter* blitter) { | 
|  | SkPoint p0, p1; | 
|  |  | 
|  | p0.set(rect.fLeft, rect.fTop); | 
|  | p1.set(rect.fRight, rect.fTop); | 
|  | SkScan::AntiHairLine(p0, p1, clip, blitter); | 
|  | p0.set(rect.fRight, rect.fBottom); | 
|  | SkScan::AntiHairLine(p0, p1, clip, blitter); | 
|  | p1.set(rect.fLeft, rect.fBottom); | 
|  | SkScan::AntiHairLine(p0, p1, clip, blitter); | 
|  | p0.set(rect.fLeft, rect.fTop); | 
|  | SkScan::AntiHairLine(p0, p1, clip, blitter); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | typedef int FDot8;  // 24.8 integer fixed point | 
|  |  | 
|  | static inline FDot8 SkFixedToFDot8(SkFixed x) { | 
|  | return (x + 0x80) >> 8; | 
|  | } | 
|  |  | 
|  | static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, | 
|  | SkBlitter* blitter) { | 
|  | SkASSERT(L < R); | 
|  |  | 
|  | if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel | 
|  | blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int left = L >> 8; | 
|  |  | 
|  | if (L & 0xFF) { | 
|  | blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))); | 
|  | left += 1; | 
|  | } | 
|  |  | 
|  | int rite = R >> 8; | 
|  | int width = rite - left; | 
|  | if (width > 0) { | 
|  | call_hline_blitter(blitter, left, top, width, alpha); | 
|  | } | 
|  | if (R & 0xFF) { | 
|  | blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter, | 
|  | bool fillInner) { | 
|  | // check for empty now that we're in our reduced precision space | 
|  | if (L >= R || T >= B) { | 
|  | return; | 
|  | } | 
|  | int top = T >> 8; | 
|  | if (top == ((B - 1) >> 8)) {   // just one scanline high | 
|  | do_scanline(L, top, R, B - T - 1, blitter); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (T & 0xFF) { | 
|  | do_scanline(L, top, R, 256 - (T & 0xFF), blitter); | 
|  | top += 1; | 
|  | } | 
|  |  | 
|  | int bot = B >> 8; | 
|  | int height = bot - top; | 
|  | if (height > 0) { | 
|  | int left = L >> 8; | 
|  | if (left == ((R - 1) >> 8)) {   // just 1-pixel wide | 
|  | blitter->blitV(left, top, height, R - L - 1); | 
|  | } else { | 
|  | if (L & 0xFF) { | 
|  | blitter->blitV(left, top, height, 256 - (L & 0xFF)); | 
|  | left += 1; | 
|  | } | 
|  | int rite = R >> 8; | 
|  | int width = rite - left; | 
|  | if (width > 0 && fillInner) { | 
|  | blitter->blitRect(left, top, width, height); | 
|  | } | 
|  | if (R & 0xFF) { | 
|  | blitter->blitV(rite, top, height, R & 0xFF); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (B & 0xFF) { | 
|  | do_scanline(L, bot, R, B & 0xFF, blitter); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void antifillrect(const SkXRect& xr, SkBlitter* blitter) { | 
|  | antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop), | 
|  | SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom), | 
|  | blitter, true); | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, | 
|  | SkBlitter* blitter) { | 
|  | if (NULL == clip) { | 
|  | antifillrect(xr, blitter); | 
|  | } else { | 
|  | SkIRect outerBounds; | 
|  | XRect_roundOut(xr, &outerBounds); | 
|  |  | 
|  | if (clip->isRect()) { | 
|  | const SkIRect& clipBounds = clip->getBounds(); | 
|  |  | 
|  | if (clipBounds.contains(outerBounds)) { | 
|  | antifillrect(xr, blitter); | 
|  | } else { | 
|  | SkXRect tmpR; | 
|  | // this keeps our original edges fractional | 
|  | XRect_set(&tmpR, clipBounds); | 
|  | if (tmpR.intersect(xr)) { | 
|  | antifillrect(tmpR, blitter); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | SkRegion::Cliperator clipper(*clip, outerBounds); | 
|  | const SkIRect&       rr = clipper.rect(); | 
|  |  | 
|  | while (!clipper.done()) { | 
|  | SkXRect  tmpR; | 
|  |  | 
|  | // this keeps our original edges fractional | 
|  | XRect_set(&tmpR, rr); | 
|  | if (tmpR.intersect(xr)) { | 
|  | antifillrect(tmpR, blitter); | 
|  | } | 
|  | clipper.next(); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip, | 
|  | SkBlitter* blitter) { | 
|  | if (clip.isBW()) { | 
|  | AntiFillXRect(xr, &clip.bwRgn(), blitter); | 
|  | } else { | 
|  | SkIRect outerBounds; | 
|  | XRect_roundOut(xr, &outerBounds); | 
|  |  | 
|  | if (clip.quickContains(outerBounds)) { | 
|  | AntiFillXRect(xr, NULL, blitter); | 
|  | } else { | 
|  | SkAAClipBlitterWrapper wrapper(clip, blitter); | 
|  | blitter = wrapper.getBlitter(); | 
|  |  | 
|  | AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter()); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /*  This guy takes a float-rect, but with the key improvement that it has | 
|  | already been clipped, so we know that it is safe to convert it into a | 
|  | XRect (fixedpoint), as it won't overflow. | 
|  | */ | 
|  | static void antifillrect(const SkRect& r, SkBlitter* blitter) { | 
|  | SkXRect xr; | 
|  |  | 
|  | XRect_set(&xr, r); | 
|  | antifillrect(xr, blitter); | 
|  | } | 
|  |  | 
|  | /*  We repeat the clipping logic of AntiFillXRect because the float rect might | 
|  | overflow if we blindly converted it to an XRect. This sucks that we have to | 
|  | repeat the clipping logic, but I don't see how to share the code/logic. | 
|  |  | 
|  | We clip r (as needed) into one or more (smaller) float rects, and then pass | 
|  | those to our version of antifillrect, which converts it into an XRect and | 
|  | then calls the blit. | 
|  | */ | 
|  | void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip, | 
|  | SkBlitter* blitter) { | 
|  | if (clip) { | 
|  | SkRect newR; | 
|  | newR.set(clip->getBounds()); | 
|  | if (!newR.intersect(origR)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | SkIRect outerBounds; | 
|  | newR.roundOut(&outerBounds); | 
|  |  | 
|  | if (clip->isRect()) { | 
|  | antifillrect(newR, blitter); | 
|  | } else { | 
|  | SkRegion::Cliperator clipper(*clip, outerBounds); | 
|  | while (!clipper.done()) { | 
|  | newR.set(clipper.rect()); | 
|  | if (newR.intersect(origR)) { | 
|  | antifillrect(newR, blitter); | 
|  | } | 
|  | clipper.next(); | 
|  | } | 
|  | } | 
|  | } else { | 
|  | antifillrect(origR, blitter); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip, | 
|  | SkBlitter* blitter) { | 
|  | if (clip.isBW()) { | 
|  | AntiFillRect(r, &clip.bwRgn(), blitter); | 
|  | } else { | 
|  | SkAAClipBlitterWrapper wrap(clip, blitter); | 
|  | AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter()); | 
|  | } | 
|  | } | 
|  |  | 
|  | /////////////////////////////////////////////////////////////////////////////// | 
|  |  | 
|  | #define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b) | 
|  |  | 
|  | // calls blitRect() if the rectangle is non-empty | 
|  | static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) { | 
|  | if (L < R && T < B) { | 
|  | blitter->blitRect(L, T, R - L, B - T); | 
|  | } | 
|  | } | 
|  |  | 
|  | static inline FDot8 SkScalarToFDot8(SkScalar x) { | 
|  | return (int)(x * 256); | 
|  | } | 
|  |  | 
|  | static inline int FDot8Floor(FDot8 x) { | 
|  | return x >> 8; | 
|  | } | 
|  |  | 
|  | static inline int FDot8Ceil(FDot8 x) { | 
|  | return (x + 0xFF) >> 8; | 
|  | } | 
|  |  | 
|  | // 1 - (1 - a)*(1 - b) | 
|  | static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) { | 
|  | // need precise rounding (not just SkAlphaMul) so that values like | 
|  | // a=228, b=252 don't overflow the result | 
|  | return SkToU8(a + b - SkAlphaMulRound(a, b)); | 
|  | } | 
|  |  | 
|  | static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, | 
|  | SkBlitter* blitter) { | 
|  | SkASSERT(L < R); | 
|  |  | 
|  | if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel | 
|  | blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L)); | 
|  | return; | 
|  | } | 
|  |  | 
|  | int left = L >> 8; | 
|  | if (L & 0xFF) { | 
|  | blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF)); | 
|  | left += 1; | 
|  | } | 
|  |  | 
|  | int rite = R >> 8; | 
|  | int width = rite - left; | 
|  | if (width > 0) { | 
|  | call_hline_blitter(blitter, left, top, width, alpha); | 
|  | } | 
|  |  | 
|  | if (R & 0xFF) { | 
|  | blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF)); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, | 
|  | SkBlitter* blitter) { | 
|  | SkASSERT(L < R && T < B); | 
|  |  | 
|  | int top = T >> 8; | 
|  | if (top == ((B - 1) >> 8)) {   // just one scanline high | 
|  | // We want the inverse of B-T, since we're the inner-stroke | 
|  | int alpha = 256 - (B - T); | 
|  | if (alpha) { | 
|  | inner_scanline(L, top, R, alpha, blitter); | 
|  | } | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (T & 0xFF) { | 
|  | inner_scanline(L, top, R, T & 0xFF, blitter); | 
|  | top += 1; | 
|  | } | 
|  |  | 
|  | int bot = B >> 8; | 
|  | int height = bot - top; | 
|  | if (height > 0) { | 
|  | if (L & 0xFF) { | 
|  | blitter->blitV(L >> 8, top, height, L & 0xFF); | 
|  | } | 
|  | if (R & 0xFF) { | 
|  | blitter->blitV(R >> 8, top, height, ~R & 0xFF); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (B & 0xFF) { | 
|  | inner_scanline(L, bot, R, ~B & 0xFF, blitter); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, | 
|  | const SkRegion* clip, SkBlitter* blitter) { | 
|  | SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); | 
|  |  | 
|  | SkScalar rx = SkScalarHalf(strokeSize.fX); | 
|  | SkScalar ry = SkScalarHalf(strokeSize.fY); | 
|  |  | 
|  | // outset by the radius | 
|  | FDot8 L = SkScalarToFDot8(r.fLeft - rx); | 
|  | FDot8 T = SkScalarToFDot8(r.fTop - ry); | 
|  | FDot8 R = SkScalarToFDot8(r.fRight + rx); | 
|  | FDot8 B = SkScalarToFDot8(r.fBottom + ry); | 
|  |  | 
|  | SkIRect outer; | 
|  | // set outer to the outer rect of the outer section | 
|  | outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); | 
|  |  | 
|  | SkBlitterClipper clipper; | 
|  | if (clip) { | 
|  | if (clip->quickReject(outer)) { | 
|  | return; | 
|  | } | 
|  | if (!clip->contains(outer)) { | 
|  | blitter = clipper.apply(blitter, clip, &outer); | 
|  | } | 
|  | // now we can ignore clip for the rest of the function | 
|  | } | 
|  |  | 
|  | // stroke the outer hull | 
|  | antifilldot8(L, T, R, B, blitter, false); | 
|  |  | 
|  | // set outer to the outer rect of the middle section | 
|  | outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B)); | 
|  |  | 
|  | // in case we lost a bit with diameter/2 | 
|  | rx = strokeSize.fX - rx; | 
|  | ry = strokeSize.fY - ry; | 
|  | // inset by the radius | 
|  | L = SkScalarToFDot8(r.fLeft + rx); | 
|  | T = SkScalarToFDot8(r.fTop + ry); | 
|  | R = SkScalarToFDot8(r.fRight - rx); | 
|  | B = SkScalarToFDot8(r.fBottom - ry); | 
|  |  | 
|  | if (L >= R || T >= B) { | 
|  | fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom, | 
|  | blitter); | 
|  | } else { | 
|  | SkIRect inner; | 
|  | // set inner to the inner rect of the middle section | 
|  | inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); | 
|  |  | 
|  | // draw the frame in 4 pieces | 
|  | fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop, | 
|  | blitter); | 
|  | fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom, | 
|  | blitter); | 
|  | fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom, | 
|  | blitter); | 
|  | fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom, | 
|  | blitter); | 
|  |  | 
|  | // now stroke the inner rect, which is similar to antifilldot8() except that | 
|  | // it treats the fractional coordinates with the inverse bias (since its | 
|  | // inner). | 
|  | innerstrokedot8(L, T, R, B, blitter); | 
|  | } | 
|  | } | 
|  |  | 
|  | void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, | 
|  | const SkRasterClip& clip, SkBlitter* blitter) { | 
|  | if (clip.isBW()) { | 
|  | AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter); | 
|  | } else { | 
|  | SkAAClipBlitterWrapper wrap(clip, blitter); | 
|  | AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter()); | 
|  | } | 
|  | } |