1 2/* 3 * Copyright 2011 The Android Open Source Project 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 10#include "SkScan.h" 11#include "SkBlitter.h" 12#include "SkColorPriv.h" 13#include "SkLineClipper.h" 14#include "SkRasterClip.h" 15#include "SkFDot6.h" 16 17/* Our attempt to compute the worst case "bounds" for the horizontal and 18 vertical cases has some numerical bug in it, and we sometimes undervalue 19 our extends. The bug is that when this happens, we will set the clip to 20 NULL (for speed), and thus draw outside of the clip by a pixel, which might 21 only look bad, but it might also access memory outside of the valid range 22 allcoated for the device bitmap. 23 24 This define enables our fix to outset our "bounds" by 1, thus avoiding the 25 chance of the bug, but at the cost of sometimes taking the rectblitter 26 case (i.e. not setting the clip to NULL) when we might not actually need 27 to. If we can improve/fix the actual calculations, then we can remove this 28 step. 29 */ 30#define OUTSET_BEFORE_CLIP_TEST true 31 32#define HLINE_STACK_BUFFER 100 33 34static inline int SmallDot6Scale(int value, int dot6) { 35 SkASSERT((int16_t)value == value); 36 SkASSERT((unsigned)dot6 <= 64); 37 return SkMulS16(value, dot6) >> 6; 38} 39 40//#define TEST_GAMMA 41 42#ifdef TEST_GAMMA 43 static uint8_t gGammaTable[256]; 44 #define ApplyGamma(table, alpha) (table)[alpha] 45 46 static void build_gamma_table() { 47 static bool gInit = false; 48 49 if (gInit == false) { 50 for (int i = 0; i < 256; i++) { 51 SkFixed n = i * 257; 52 n += n >> 15; 53 SkASSERT(n >= 0 && n <= SK_Fixed1); 54 n = SkFixedSqrt(n); 55 n = n * 255 >> 16; 56 // SkDebugf("morph %d -> %d\n", i, n); 57 gGammaTable[i] = SkToU8(n); 58 } 59 gInit = true; 60 } 61 } 62#else 63 #define ApplyGamma(table, alpha) SkToU8(alpha) 64#endif 65 66/////////////////////////////////////////////////////////////////////////////// 67 68static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count, 69 U8CPU alpha) { 70 SkASSERT(count > 0); 71 72 int16_t runs[HLINE_STACK_BUFFER + 1]; 73 uint8_t aa[HLINE_STACK_BUFFER]; 74 75 aa[0] = ApplyGamma(gGammaTable, alpha); 76 do { 77 int n = count; 78 if (n > HLINE_STACK_BUFFER) { 79 n = HLINE_STACK_BUFFER; 80 } 81 runs[0] = SkToS16(n); 82 runs[n] = 0; 83 blitter->blitAntiH(x, y, aa, runs); 84 x += n; 85 count -= n; 86 } while (count > 0); 87} 88 89class SkAntiHairBlitter { 90public: 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 103private: 104 SkBlitter* fBlitter; 105}; 106 107class HLine_SkAntiHairBlitter : public SkAntiHairBlitter { 108public: 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 154class Horish_SkAntiHairBlitter : public SkAntiHairBlitter { 155public: 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 184class VLine_SkAntiHairBlitter : public SkAntiHairBlitter { 185public: 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 225class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter { 226public: 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 252static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) { 253 SkASSERT((a << 16 >> 16) == a); 254 SkASSERT(b != 0); 255 return (a << 16) / b; 256} 257 258#define SkBITCOUNT(x) (sizeof(x) << 3) 259 260#if 1 261// returns high-bit set iff x==0x8000... 262static inline int bad_int(int x) { 263 return x & -x; 264} 265 266static 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 270static inline int good_int(int x) { 271 return x ^ (1 << (SkBITCOUNT(x) - 1)); 272} 273 274static 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 280static 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 */ 292static 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 305static 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 = NULL; 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 = NULL; 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 = NULL; 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 523void 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 == NULL || !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, NULL, blitter); 599 } 600} 601 602void 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 616typedef int FDot8; // 24.8 integer fixed point 617 618static inline FDot8 SkFixedToFDot8(SkFixed x) { 619 return (x + 0x80) >> 8; 620} 621 622static 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 648static 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 692static 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 700void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, 701 SkBlitter* blitter) { 702 if (NULL == 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 739void 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, NULL, 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*/ 762static 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*/ 777void 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 805void 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 820static 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 826static inline FDot8 SkScalarToFDot8(SkScalar x) { 827 return (int)(x * 256); 828} 829 830static inline int FDot8Floor(FDot8 x) { 831 return x >> 8; 832} 833 834static inline int FDot8Ceil(FDot8 x) { 835 return (x + 0xFF) >> 8; 836} 837 838// 1 - (1 - a)*(1 - b) 839static 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 845static 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 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L)); 851 return; 852 } 853 854 int left = L >> 8; 855 if (L & 0xFF) { 856 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF)); 857 left += 1; 858 } 859 860 int rite = R >> 8; 861 int width = rite - left; 862 if (width > 0) { 863 call_hline_blitter(blitter, left, top, width, alpha); 864 } 865 866 if (R & 0xFF) { 867 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF)); 868 } 869} 870 871static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, 872 SkBlitter* blitter) { 873 SkASSERT(L < R && T < B); 874 875 int top = T >> 8; 876 if (top == ((B - 1) >> 8)) { // just one scanline high 877 // We want the inverse of B-T, since we're the inner-stroke 878 int alpha = 256 - (B - T); 879 if (alpha) { 880 inner_scanline(L, top, R, alpha, blitter); 881 } 882 return; 883 } 884 885 if (T & 0xFF) { 886 inner_scanline(L, top, R, T & 0xFF, blitter); 887 top += 1; 888 } 889 890 int bot = B >> 8; 891 int height = bot - top; 892 if (height > 0) { 893 if (L & 0xFF) { 894 blitter->blitV(L >> 8, top, height, L & 0xFF); 895 } 896 if (R & 0xFF) { 897 blitter->blitV(R >> 8, top, height, ~R & 0xFF); 898 } 899 } 900 901 if (B & 0xFF) { 902 inner_scanline(L, bot, R, ~B & 0xFF, blitter); 903 } 904} 905 906static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) { 907 SkASSERT(edge1 <= edge2); 908 909 if (FDot8Floor(edge1) == FDot8Floor(edge2)) { 910 edge2 -= (edge1 & 0xFF); 911 edge1 &= ~0xFF; 912 } 913} 914 915void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 916 const SkRegion* clip, SkBlitter* blitter) { 917 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); 918 919 SkScalar rx = SkScalarHalf(strokeSize.fX); 920 SkScalar ry = SkScalarHalf(strokeSize.fY); 921 922 // outset by the radius 923 FDot8 outerL = SkScalarToFDot8(r.fLeft - rx); 924 FDot8 outerT = SkScalarToFDot8(r.fTop - ry); 925 FDot8 outerR = SkScalarToFDot8(r.fRight + rx); 926 FDot8 outerB = SkScalarToFDot8(r.fBottom + ry); 927 928 SkIRect outer; 929 // set outer to the outer rect of the outer section 930 outer.set(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB)); 931 932 SkBlitterClipper clipper; 933 if (clip) { 934 if (clip->quickReject(outer)) { 935 return; 936 } 937 if (!clip->contains(outer)) { 938 blitter = clipper.apply(blitter, clip, &outer); 939 } 940 // now we can ignore clip for the rest of the function 941 } 942 943 // in case we lost a bit with diameter/2 944 rx = strokeSize.fX - rx; 945 ry = strokeSize.fY - ry; 946 947 // inset by the radius 948 FDot8 innerL = SkScalarToFDot8(r.fLeft + rx); 949 FDot8 innerT = SkScalarToFDot8(r.fTop + ry); 950 FDot8 innerR = SkScalarToFDot8(r.fRight - rx); 951 FDot8 innerB = SkScalarToFDot8(r.fBottom - ry); 952 953 // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel 954 // edge. This ensures that the general rect stroking logic below 955 // a) doesn't blit the same scanline twice 956 // b) computes the correct coverage when both edges fall within the same pixel 957 if (strokeSize.fX < 1 || strokeSize.fY < 1) { 958 align_thin_stroke(outerL, innerL); 959 align_thin_stroke(outerT, innerT); 960 align_thin_stroke(innerR, outerR); 961 align_thin_stroke(innerB, outerB); 962 } 963 964 // stroke the outer hull 965 antifilldot8(outerL, outerT, outerR, outerB, blitter, false); 966 967 // set outer to the outer rect of the middle section 968 outer.set(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB)); 969 970 if (innerL >= innerR || innerT >= innerB) { 971 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom, 972 blitter); 973 } else { 974 SkIRect inner; 975 // set inner to the inner rect of the middle section 976 inner.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB)); 977 978 // draw the frame in 4 pieces 979 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop, 980 blitter); 981 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom, 982 blitter); 983 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom, 984 blitter); 985 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom, 986 blitter); 987 988 // now stroke the inner rect, which is similar to antifilldot8() except that 989 // it treats the fractional coordinates with the inverse bias (since its 990 // inner). 991 innerstrokedot8(innerL, innerT, innerR, innerB, blitter); 992 } 993} 994 995void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 996 const SkRasterClip& clip, SkBlitter* blitter) { 997 if (clip.isBW()) { 998 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter); 999 } else { 1000 SkAAClipBlitterWrapper wrap(clip, blitter); 1001 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter()); 1002 } 1003} 1004