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