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