SkScan_Antihair.cpp revision bbbe9ed59ee1d3077fa4e6368a4a7294240a5ec6
1 2/* 3 * Copyright 2011 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkScan.h" 11#include "SkBlitter.h" 12#include "SkColorPriv.h" 13#include "SkLineClipper.h" 14#include "SkRasterClip.h" 15#include "SkFDot6.h" 16 17/* Our attempt to compute the worst case "bounds" for the horizontal and 18 vertical cases has some numerical bug in it, and we sometimes undervalue 19 our extends. The bug is that when this happens, we will set the clip to 20 NULL (for speed), and thus draw outside of the clip by a pixel, which might 21 only look bad, but it might also access memory outside of the valid range 22 allcoated for the device bitmap. 23 24 This define enables our fix to outset our "bounds" by 1, thus avoiding the 25 chance of the bug, but at the cost of sometimes taking the rectblitter 26 case (i.e. not setting the clip to NULL) when we might not actually need 27 to. If we can improve/fix the actual calculations, then we can remove this 28 step. 29 */ 30#define OUTSET_BEFORE_CLIP_TEST true 31 32#define HLINE_STACK_BUFFER 100 33 34static inline int SmallDot6Scale(int value, int dot6) { 35 SkASSERT((int16_t)value == value); 36 SkASSERT((unsigned)dot6 <= 64); 37 return SkMulS16(value, dot6) >> 6; 38} 39 40//#define TEST_GAMMA 41 42#ifdef TEST_GAMMA 43 static uint8_t gGammaTable[256]; 44 #define ApplyGamma(table, alpha) (table)[alpha] 45 46 static void build_gamma_table() { 47 static bool gInit = false; 48 49 if (gInit == false) { 50 for (int i = 0; i < 256; i++) { 51 SkFixed n = i * 257; 52 n += n >> 15; 53 SkASSERT(n >= 0 && n <= SK_Fixed1); 54 n = SkFixedSqrt(n); 55 n = n * 255 >> 16; 56 // SkDebugf("morph %d -> %d\n", i, n); 57 gGammaTable[i] = SkToU8(n); 58 } 59 gInit = true; 60 } 61 } 62#else 63 #define ApplyGamma(table, alpha) SkToU8(alpha) 64#endif 65 66/////////////////////////////////////////////////////////////////////////////// 67 68static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, 69 U8CPU alpha) { 70 SkASSERT(count > 0); 71 72 int16_t runs[HLINE_STACK_BUFFER + 1]; 73 uint8_t aa[HLINE_STACK_BUFFER]; 74 75 aa[0] = ApplyGamma(gGammaTable, alpha); 76 do { 77 int n = count; 78 if (n > HLINE_STACK_BUFFER) { 79 n = HLINE_STACK_BUFFER; 80 } 81 runs[0] = SkToS16(n); 82 runs[n] = 0; 83 blitter->blitAntiH(x, y, aa, runs); 84 x += n; 85 count -= n; 86 } while (count > 0); 87} 88 89static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, 90 SkBlitter* blitter, int mod64) { 91 SkASSERT(x < stopx); 92 int count = stopx - x; 93 fy += SK_Fixed1/2; 94 95 int y = fy >> 16; 96 uint8_t a = (uint8_t)(fy >> 8); 97 98 // lower line 99 unsigned ma = SmallDot6Scale(a, mod64); 100 if (ma) { 101 call_hline_blitter(blitter, x, y, count, ma); 102 } 103 104 // upper line 105 ma = SmallDot6Scale(255 - a, mod64); 106 if (ma) { 107 call_hline_blitter(blitter, x, y - 1, count, ma); 108 } 109 110 return fy - SK_Fixed1/2; 111} 112 113static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, 114 SkBlitter* blitter, int mod64) { 115 SkASSERT(x < stopx); 116 117#ifdef TEST_GAMMA 118 const uint8_t* gamma = gGammaTable; 119#endif 120 int16_t runs[2]; 121 uint8_t aa[1]; 122 123 runs[0] = 1; 124 runs[1] = 0; 125 126 fy += SK_Fixed1/2; 127 do { 128 int lower_y = fy >> 16; 129 uint8_t a = (uint8_t)(fy >> 8); 130 unsigned ma = SmallDot6Scale(a, mod64); 131 if (ma) { 132 aa[0] = ApplyGamma(gamma, ma); 133 blitter->blitAntiH(x, lower_y, aa, runs); 134 // the clipping blitters might edit runs, but should not affect us 135 SkASSERT(runs[0] == 1); 136 SkASSERT(runs[1] == 0); 137 } 138 ma = SmallDot6Scale(255 - a, mod64); 139 if (ma) { 140 aa[0] = ApplyGamma(gamma, ma); 141 blitter->blitAntiH(x, lower_y - 1, aa, runs); 142 // the clipping blitters might edit runs, but should not affect us 143 SkASSERT(runs[0] == 1); 144 SkASSERT(runs[1] == 0); 145 } 146 fy += dy; 147 } while (++x < stopx); 148 149 return fy - SK_Fixed1/2; 150} 151 152static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, 153 SkBlitter* blitter, int mod64) { 154 SkASSERT(y < stopy); 155 fx += SK_Fixed1/2; 156 157 int x = fx >> 16; 158 int a = (uint8_t)(fx >> 8); 159 160 unsigned ma = SmallDot6Scale(a, mod64); 161 if (ma) { 162 blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma)); 163 } 164 ma = SmallDot6Scale(255 - a, mod64); 165 if (ma) { 166 blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma)); 167 } 168 169 return fx - SK_Fixed1/2; 170} 171 172static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, 173 SkBlitter* blitter, int mod64) { 174 SkASSERT(y < stopy); 175#ifdef TEST_GAMMA 176 const uint8_t* gamma = gGammaTable; 177#endif 178 int16_t runs[3]; 179 uint8_t aa[2]; 180 181 runs[0] = 1; 182 runs[2] = 0; 183 184 fx += SK_Fixed1/2; 185 do { 186 int x = fx >> 16; 187 uint8_t a = (uint8_t)(fx >> 8); 188 189 aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64)); 190 aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64)); 191 // the clippng blitters might overwrite this guy, so we have to reset it each time 192 runs[1] = 1; 193 blitter->blitAntiH(x - 1, y, aa, runs); 194 // the clipping blitters might edit runs, but should not affect us 195 SkASSERT(runs[0] == 1); 196 SkASSERT(runs[2] == 0); 197 fx += dx; 198 } while (++y < stopy); 199 200 return fx - SK_Fixed1/2; 201} 202 203typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, 204 SkFixed slope, SkBlitter*, int); 205 206static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) { 207 SkASSERT((a << 16 >> 16) == a); 208 SkASSERT(b != 0); 209 return (a << 16) / b; 210} 211 212#define SkBITCOUNT(x) (sizeof(x) << 3) 213 214#if 1 215// returns high-bit set iff x==0x8000... 216static inline int bad_int(int x) { 217 return x & -x; 218} 219 220static int any_bad_ints(int a, int b, int c, int d) { 221 return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1); 222} 223#else 224static inline int good_int(int x) { 225 return x ^ (1 << (SkBITCOUNT(x) - 1)); 226} 227 228static int any_bad_ints(int a, int b, int c, int d) { 229 return !(good_int(a) & good_int(b) & good_int(c) & good_int(d)); 230} 231#endif 232 233static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, 234 const SkIRect* clip, SkBlitter* blitter) { 235 // check for integer NaN (0x80000000) which we can't handle (can't negate it) 236 // It appears typically from a huge float (inf or nan) being converted to int. 237 // If we see it, just don't draw. 238 if (any_bad_ints(x0, y0, x1, y1)) { 239 return; 240 } 241 242 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) { 243 /* instead of (x0 + x1) >> 1, we shift each separately. This is less 244 precise, but avoids overflowing the intermediate result if the 245 values are huge. A better fix might be to clip the original pts 246 directly (i.e. do the divide), so we don't spend time subdividing 247 huge lines at all. 248 */ 249 int hx = (x0 >> 1) + (x1 >> 1); 250 int hy = (y0 >> 1) + (y1 >> 1); 251 do_anti_hairline(x0, y0, hx, hy, clip, blitter); 252 do_anti_hairline(hx, hy, x1, y1, clip, blitter); 253 return; 254 } 255 256 int scaleStart, scaleStop; 257 int istart, istop; 258 SkFixed fstart, slope; 259 LineProc proc; 260 261 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal 262 if (x0 > x1) { // we want to go left-to-right 263 SkTSwap<SkFDot6>(x0, x1); 264 SkTSwap<SkFDot6>(y0, y1); 265 } 266 267 istart = SkFDot6Floor(x0); 268 istop = SkFDot6Ceil(x1); 269 fstart = SkFDot6ToFixed(y0); 270 if (y0 == y1) { // completely horizontal, take fast case 271 slope = 0; 272 proc = hline; 273 } else { 274 slope = fastfixdiv(y1 - y0, x1 - x0); 275 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); 276 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6; 277 proc = horish; 278 } 279 280 SkASSERT(istop > istart); 281 if (istop - istart == 1) { 282 scaleStart = x1 - x0; 283 SkASSERT(scaleStart >= 0 && scaleStart <= 64); 284 scaleStop = 0; 285 } else { 286 scaleStart = 64 - (x0 & 63); 287 scaleStop = x1 & 63; 288 } 289 290 if (clip){ 291 if (istart >= clip->fRight || istop <= clip->fLeft) { 292 return; 293 } 294 if (istart < clip->fLeft) { 295 fstart += slope * (clip->fLeft - istart); 296 istart = clip->fLeft; 297 scaleStart = 64; 298 } 299 if (istop > clip->fRight) { 300 istop = clip->fRight; 301 scaleStop = 0; // so we don't draw this last column 302 } 303 304 SkASSERT(istart <= istop); 305 if (istart == istop) { 306 return; 307 } 308 // now test if our Y values are completely inside the clip 309 int top, bottom; 310 if (slope >= 0) { // T2B 311 top = SkFixedFloor(fstart - SK_FixedHalf); 312 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); 313 } else { // B2T 314 bottom = SkFixedCeil(fstart + SK_FixedHalf); 315 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); 316 } 317#ifdef OUTSET_BEFORE_CLIP_TEST 318 top -= 1; 319 bottom += 1; 320#endif 321 if (top >= clip->fBottom || bottom <= clip->fTop) { 322 return; 323 } 324 if (clip->fTop <= top && clip->fBottom >= bottom) { 325 clip = NULL; 326 } 327 } 328 } else { // mostly vertical 329 if (y0 > y1) { // we want to go top-to-bottom 330 SkTSwap<SkFDot6>(x0, x1); 331 SkTSwap<SkFDot6>(y0, y1); 332 } 333 334 istart = SkFDot6Floor(y0); 335 istop = SkFDot6Ceil(y1); 336 fstart = SkFDot6ToFixed(x0); 337 if (x0 == x1) { 338 if (y0 == y1) { // are we zero length? 339 return; // nothing to do 340 } 341 slope = 0; 342 proc = vline; 343 } else { 344 slope = fastfixdiv(x1 - x0, y1 - y0); 345 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); 346 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6; 347 proc = vertish; 348 } 349 350 SkASSERT(istop > istart); 351 if (istop - istart == 1) { 352 scaleStart = y1 - y0; 353 SkASSERT(scaleStart >= 0 && scaleStart <= 64); 354 scaleStop = 0; 355 } else { 356 scaleStart = 64 - (y0 & 63); 357 scaleStop = y1 & 63; 358 } 359 360 if (clip) { 361 if (istart >= clip->fBottom || istop <= clip->fTop) { 362 return; 363 } 364 if (istart < clip->fTop) { 365 fstart += slope * (clip->fTop - istart); 366 istart = clip->fTop; 367 scaleStart = 64; 368 } 369 if (istop > clip->fBottom) { 370 istop = clip->fBottom; 371 scaleStop = 0; // so we don't draw this last row 372 } 373 374 SkASSERT(istart <= istop); 375 if (istart == istop) 376 return; 377 378 // now test if our X values are completely inside the clip 379 int left, right; 380 if (slope >= 0) { // L2R 381 left = SkFixedFloor(fstart - SK_FixedHalf); 382 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); 383 } else { // R2L 384 right = SkFixedCeil(fstart + SK_FixedHalf); 385 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); 386 } 387#ifdef OUTSET_BEFORE_CLIP_TEST 388 left -= 1; 389 right += 1; 390#endif 391 if (left >= clip->fRight || right <= clip->fLeft) { 392 return; 393 } 394 if (clip->fLeft <= left && clip->fRight >= right) { 395 clip = NULL; 396 } 397 } 398 } 399 400 SkRectClipBlitter rectClipper; 401 if (clip) { 402 rectClipper.init(blitter, *clip); 403 blitter = &rectClipper; 404 } 405 406 fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart); 407 istart += 1; 408 int fullSpans = istop - istart - (scaleStop > 0); 409 if (fullSpans > 0) { 410 fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64); 411 } 412 if (scaleStop > 0) { 413 proc(istop - 1, istop, fstart, slope, blitter, scaleStop); 414 } 415} 416 417void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1, 418 const SkRegion* clip, SkBlitter* blitter) { 419 if (clip && clip->isEmpty()) { 420 return; 421 } 422 423 SkASSERT(clip == NULL || !clip->getBounds().isEmpty()); 424 425#ifdef TEST_GAMMA 426 build_gamma_table(); 427#endif 428 429 SkPoint pts[2] = { pt0, pt1 }; 430 431 if (clip) { 432 SkRect clipBounds; 433 clipBounds.set(clip->getBounds()); 434 /* We perform integral clipping later on, but we do a scalar clip first 435 to ensure that our coordinates are expressible in fixed/integers. 436 437 antialiased hairlines can draw up to 1/2 of a pixel outside of 438 their bounds, so we need to outset the clip before calling the 439 clipper. To make the numerics safer, we outset by a whole pixel, 440 since the 1/2 pixel boundary is important to the antihair blitter, 441 we don't want to risk numerical fate by chopping on that edge. 442 */ 443 clipBounds.inset(-SK_Scalar1, -SK_Scalar1); 444 445 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) { 446 return; 447 } 448 } 449 450 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); 451 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); 452 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); 453 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); 454 455 if (clip) { 456 SkFDot6 left = SkMin32(x0, x1); 457 SkFDot6 top = SkMin32(y0, y1); 458 SkFDot6 right = SkMax32(x0, x1); 459 SkFDot6 bottom = SkMax32(y0, y1); 460 SkIRect ir; 461 462 ir.set( SkFDot6Floor(left) - 1, 463 SkFDot6Floor(top) - 1, 464 SkFDot6Ceil(right) + 1, 465 SkFDot6Ceil(bottom) + 1); 466 467 if (clip->quickReject(ir)) { 468 return; 469 } 470 if (!clip->quickContains(ir)) { 471 SkRegion::Cliperator iter(*clip, ir); 472 const SkIRect* r = &iter.rect(); 473 474 while (!iter.done()) { 475 do_anti_hairline(x0, y0, x1, y1, r, blitter); 476 iter.next(); 477 } 478 return; 479 } 480 // fall through to no-clip case 481 } 482 do_anti_hairline(x0, y0, x1, y1, NULL, blitter); 483} 484 485void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip, 486 SkBlitter* blitter) { 487 SkPoint p0, p1; 488 489 p0.set(rect.fLeft, rect.fTop); 490 p1.set(rect.fRight, rect.fTop); 491 SkScan::AntiHairLine(p0, p1, clip, blitter); 492 p0.set(rect.fRight, rect.fBottom); 493 SkScan::AntiHairLine(p0, p1, clip, blitter); 494 p1.set(rect.fLeft, rect.fBottom); 495 SkScan::AntiHairLine(p0, p1, clip, blitter); 496 p0.set(rect.fLeft, rect.fTop); 497 SkScan::AntiHairLine(p0, p1, clip, blitter); 498} 499 500/////////////////////////////////////////////////////////////////////////////// 501 502typedef int FDot8; // 24.8 integer fixed point 503 504static inline FDot8 SkFixedToFDot8(SkFixed x) { 505 return (x + 0x80) >> 8; 506} 507 508static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, 509 SkBlitter* blitter) { 510 SkASSERT(L < R); 511 512 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel 513 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)); 514 return; 515 } 516 517 int left = L >> 8; 518 519 if (L & 0xFF) { 520 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))); 521 left += 1; 522 } 523 524 int rite = R >> 8; 525 int width = rite - left; 526 if (width > 0) { 527 call_hline_blitter(blitter, left, top, width, alpha); 528 } 529 if (R & 0xFF) { 530 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); 531 } 532} 533 534static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter, 535 bool fillInner) { 536 // check for empty now that we're in our reduced precision space 537 if (L >= R || T >= B) { 538 return; 539 } 540 int top = T >> 8; 541 if (top == ((B - 1) >> 8)) { // just one scanline high 542 do_scanline(L, top, R, B - T - 1, blitter); 543 return; 544 } 545 546 if (T & 0xFF) { 547 do_scanline(L, top, R, 256 - (T & 0xFF), blitter); 548 top += 1; 549 } 550 551 int bot = B >> 8; 552 int height = bot - top; 553 if (height > 0) { 554 int left = L >> 8; 555 if (left == ((R - 1) >> 8)) { // just 1-pixel wide 556 blitter->blitV(left, top, height, R - L - 1); 557 } else { 558 if (L & 0xFF) { 559 blitter->blitV(left, top, height, 256 - (L & 0xFF)); 560 left += 1; 561 } 562 int rite = R >> 8; 563 int width = rite - left; 564 if (width > 0 && fillInner) { 565 blitter->blitRect(left, top, width, height); 566 } 567 if (R & 0xFF) { 568 blitter->blitV(rite, top, height, R & 0xFF); 569 } 570 } 571 } 572 573 if (B & 0xFF) { 574 do_scanline(L, bot, R, B & 0xFF, blitter); 575 } 576} 577 578static void antifillrect(const SkXRect& xr, SkBlitter* blitter) { 579 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop), 580 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom), 581 blitter, true); 582} 583 584/////////////////////////////////////////////////////////////////////////////// 585 586void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, 587 SkBlitter* blitter) { 588 if (NULL == clip) { 589 antifillrect(xr, blitter); 590 } else { 591 SkIRect outerBounds; 592 XRect_roundOut(xr, &outerBounds); 593 594 if (clip->isRect()) { 595 const SkIRect& clipBounds = clip->getBounds(); 596 597 if (clipBounds.contains(outerBounds)) { 598 antifillrect(xr, blitter); 599 } else { 600 SkXRect tmpR; 601 // this keeps our original edges fractional 602 XRect_set(&tmpR, clipBounds); 603 if (tmpR.intersect(xr)) { 604 antifillrect(tmpR, blitter); 605 } 606 } 607 } else { 608 SkRegion::Cliperator clipper(*clip, outerBounds); 609 const SkIRect& rr = clipper.rect(); 610 611 while (!clipper.done()) { 612 SkXRect tmpR; 613 614 // this keeps our original edges fractional 615 XRect_set(&tmpR, rr); 616 if (tmpR.intersect(xr)) { 617 antifillrect(tmpR, blitter); 618 } 619 clipper.next(); 620 } 621 } 622 } 623} 624 625void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip, 626 SkBlitter* blitter) { 627 if (clip.isBW()) { 628 AntiFillXRect(xr, &clip.bwRgn(), blitter); 629 } else { 630 SkIRect outerBounds; 631 XRect_roundOut(xr, &outerBounds); 632 633 if (clip.quickContains(outerBounds)) { 634 AntiFillXRect(xr, NULL, blitter); 635 } else { 636 SkAAClipBlitterWrapper wrapper(clip, blitter); 637 blitter = wrapper.getBlitter(); 638 639 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter()); 640 } 641 } 642} 643 644#ifdef SK_SCALAR_IS_FLOAT 645 646/* This guy takes a float-rect, but with the key improvement that it has 647 already been clipped, so we know that it is safe to convert it into a 648 XRect (fixedpoint), as it won't overflow. 649*/ 650static void antifillrect(const SkRect& r, SkBlitter* blitter) { 651 SkXRect xr; 652 653 XRect_set(&xr, r); 654 antifillrect(xr, blitter); 655} 656 657/* We repeat the clipping logic of AntiFillXRect because the float rect might 658 overflow if we blindly converted it to an XRect. This sucks that we have to 659 repeat the clipping logic, but I don't see how to share the code/logic. 660 661 We clip r (as needed) into one or more (smaller) float rects, and then pass 662 those to our version of antifillrect, which converts it into an XRect and 663 then calls the blit. 664*/ 665void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip, 666 SkBlitter* blitter) { 667 if (clip) { 668 SkRect newR; 669 newR.set(clip->getBounds()); 670 if (!newR.intersect(origR)) { 671 return; 672 } 673 674 SkIRect outerBounds; 675 newR.roundOut(&outerBounds); 676 677 if (clip->isRect()) { 678 antifillrect(newR, blitter); 679 } else { 680 SkRegion::Cliperator clipper(*clip, outerBounds); 681 while (!clipper.done()) { 682 newR.set(clipper.rect()); 683 if (newR.intersect(origR)) { 684 antifillrect(newR, blitter); 685 } 686 clipper.next(); 687 } 688 } 689 } else { 690 antifillrect(origR, blitter); 691 } 692} 693 694void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip, 695 SkBlitter* blitter) { 696 if (clip.isBW()) { 697 AntiFillRect(r, &clip.bwRgn(), blitter); 698 } else { 699 SkAAClipBlitterWrapper wrap(clip, blitter); 700 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter()); 701 } 702} 703 704#endif // SK_SCALAR_IS_FLOAT 705 706/////////////////////////////////////////////////////////////////////////////// 707 708#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b) 709 710// calls blitRect() if the rectangle is non-empty 711static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) { 712 if (L < R && T < B) { 713 blitter->blitRect(L, T, R - L, B - T); 714 } 715} 716 717static inline FDot8 SkScalarToFDot8(SkScalar x) { 718#ifdef SK_SCALAR_IS_FLOAT 719 return (int)(x * 256); 720#else 721 return x >> 8; 722#endif 723} 724 725static inline int FDot8Floor(FDot8 x) { 726 return x >> 8; 727} 728 729static inline int FDot8Ceil(FDot8 x) { 730 return (x + 0xFF) >> 8; 731} 732 733// 1 - (1 - a)*(1 - b) 734static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) { 735 // need precise rounding (not just SkAlphaMul) so that values like 736 // a=228, b=252 don't overflow the result 737 return SkToU8(a + b - SkAlphaMulRound(a, b)); 738} 739 740static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, 741 SkBlitter* blitter) { 742 SkASSERT(L < R); 743 744 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel 745 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L)); 746 return; 747 } 748 749 int left = L >> 8; 750 if (L & 0xFF) { 751 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF)); 752 left += 1; 753 } 754 755 int rite = R >> 8; 756 int width = rite - left; 757 if (width > 0) { 758 call_hline_blitter(blitter, left, top, width, alpha); 759 } 760 761 if (R & 0xFF) { 762 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF)); 763 } 764} 765 766static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, 767 SkBlitter* blitter) { 768 SkASSERT(L < R && T < B); 769 770 int top = T >> 8; 771 if (top == ((B - 1) >> 8)) { // just one scanline high 772 inner_scanline(L, top, R, B - T, blitter); 773 return; 774 } 775 776 if (T & 0xFF) { 777 inner_scanline(L, top, R, T & 0xFF, blitter); 778 top += 1; 779 } 780 781 int bot = B >> 8; 782 int height = bot - top; 783 if (height > 0) { 784 if (L & 0xFF) { 785 blitter->blitV(L >> 8, top, height, L & 0xFF); 786 } 787 if (R & 0xFF) { 788 blitter->blitV(R >> 8, top, height, ~R & 0xFF); 789 } 790 } 791 792 if (B & 0xFF) { 793 inner_scanline(L, bot, R, ~B & 0xFF, blitter); 794 } 795} 796 797void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 798 const SkRegion* clip, SkBlitter* blitter) { 799 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); 800 801 SkScalar rx = SkScalarHalf(strokeSize.fX); 802 SkScalar ry = SkScalarHalf(strokeSize.fY); 803 804 // outset by the radius 805 FDot8 L = SkScalarToFDot8(r.fLeft - rx); 806 FDot8 T = SkScalarToFDot8(r.fTop - ry); 807 FDot8 R = SkScalarToFDot8(r.fRight + rx); 808 FDot8 B = SkScalarToFDot8(r.fBottom + ry); 809 810 SkIRect outer; 811 // set outer to the outer rect of the outer section 812 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); 813 814 SkBlitterClipper clipper; 815 if (clip) { 816 if (clip->quickReject(outer)) { 817 return; 818 } 819 if (!clip->contains(outer)) { 820 blitter = clipper.apply(blitter, clip, &outer); 821 } 822 // now we can ignore clip for the rest of the function 823 } 824 825 // stroke the outer hull 826 antifilldot8(L, T, R, B, blitter, false); 827 828 // set outer to the outer rect of the middle section 829 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B)); 830 831 // in case we lost a bit with diameter/2 832 rx = strokeSize.fX - rx; 833 ry = strokeSize.fY - ry; 834 // inset by the radius 835 L = SkScalarToFDot8(r.fLeft + rx); 836 T = SkScalarToFDot8(r.fTop + ry); 837 R = SkScalarToFDot8(r.fRight - rx); 838 B = SkScalarToFDot8(r.fBottom - ry); 839 840 if (L >= R || T >= B) { 841 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom, 842 blitter); 843 } else { 844 SkIRect inner; 845 // set inner to the inner rect of the middle section 846 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); 847 848 // draw the frame in 4 pieces 849 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop, 850 blitter); 851 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom, 852 blitter); 853 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom, 854 blitter); 855 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom, 856 blitter); 857 858 // now stroke the inner rect, which is similar to antifilldot8() except that 859 // it treats the fractional coordinates with the inverse bias (since its 860 // inner). 861 innerstrokedot8(L, T, R, B, blitter); 862 } 863} 864 865void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 866 const SkRasterClip& clip, SkBlitter* blitter) { 867 if (clip.isBW()) { 868 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter); 869 } else { 870 SkAAClipBlitterWrapper wrap(clip, blitter); 871 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter()); 872 } 873} 874 875