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 +}