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