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