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 89static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/, 90 SkBlitter* blitter, int mod64) { 91 SkASSERT(x < stopx); 92 int count = stopx - x; 93 fy += SK_Fixed1/2; 94 95 int y = fy >> 16; 96 uint8_t a = (uint8_t)(fy >> 8); 97 98 // lower line 99 unsigned ma = SmallDot6Scale(a, mod64); 100 if (ma) { 101 call_hline_blitter(blitter, x, y, count, ma); 102 } 103 104 // upper line 105 ma = SmallDot6Scale(255 - a, mod64); 106 if (ma) { 107 call_hline_blitter(blitter, x, y - 1, count, ma); 108 } 109 110 return fy - SK_Fixed1/2; 111} 112 113static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy, 114 SkBlitter* blitter, int mod64) { 115 SkASSERT(x < stopx); 116 117#ifdef TEST_GAMMA 118 const uint8_t* gamma = gGammaTable; 119#endif 120 int16_t runs[2]; 121 uint8_t aa[1]; 122 123 runs[0] = 1; 124 runs[1] = 0; 125 126 fy += SK_Fixed1/2; 127 do { 128 int lower_y = fy >> 16; 129 uint8_t a = (uint8_t)(fy >> 8); 130 unsigned ma = SmallDot6Scale(a, mod64); 131 if (ma) { 132 aa[0] = ApplyGamma(gamma, ma); 133 blitter->blitAntiH(x, lower_y, aa, runs); 134 // the clipping blitters might edit runs, but should not affect us 135 SkASSERT(runs[0] == 1); 136 SkASSERT(runs[1] == 0); 137 } 138 ma = SmallDot6Scale(255 - a, mod64); 139 if (ma) { 140 aa[0] = ApplyGamma(gamma, ma); 141 blitter->blitAntiH(x, lower_y - 1, aa, runs); 142 // the clipping blitters might edit runs, but should not affect us 143 SkASSERT(runs[0] == 1); 144 SkASSERT(runs[1] == 0); 145 } 146 fy += dy; 147 } while (++x < stopx); 148 149 return fy - SK_Fixed1/2; 150} 151 152static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/, 153 SkBlitter* blitter, int mod64) { 154 SkASSERT(y < stopy); 155 fx += SK_Fixed1/2; 156 157 int x = fx >> 16; 158 int a = (uint8_t)(fx >> 8); 159 160 unsigned ma = SmallDot6Scale(a, mod64); 161 if (ma) { 162 blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma)); 163 } 164 ma = SmallDot6Scale(255 - a, mod64); 165 if (ma) { 166 blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma)); 167 } 168 169 return fx - SK_Fixed1/2; 170} 171 172static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx, 173 SkBlitter* blitter, int mod64) { 174 SkASSERT(y < stopy); 175#ifdef TEST_GAMMA 176 const uint8_t* gamma = gGammaTable; 177#endif 178 int16_t runs[3]; 179 uint8_t aa[2]; 180 181 runs[0] = 1; 182 runs[2] = 0; 183 184 fx += SK_Fixed1/2; 185 do { 186 int x = fx >> 16; 187 uint8_t a = (uint8_t)(fx >> 8); 188 189 aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64)); 190 aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64)); 191 // the clippng blitters might overwrite this guy, so we have to reset it each time 192 runs[1] = 1; 193 blitter->blitAntiH(x - 1, y, aa, runs); 194 // the clipping blitters might edit runs, but should not affect us 195 SkASSERT(runs[0] == 1); 196 SkASSERT(runs[2] == 0); 197 fx += dx; 198 } while (++y < stopy); 199 200 return fx - SK_Fixed1/2; 201} 202 203typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart, 204 SkFixed slope, SkBlitter*, int); 205 206static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) { 207 SkASSERT((a << 16 >> 16) == a); 208 SkASSERT(b != 0); 209 return (a << 16) / b; 210} 211 212static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, 213 const SkIRect* clip, SkBlitter* blitter) { 214 // check that we're no larger than 511 pixels (so we can do a faster div). 215 // if we are, subdivide and call again 216 217 if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) { 218 /* instead of (x0 + x1) >> 1, we shift each separately. This is less 219 precise, but avoids overflowing the intermediate result if the 220 values are huge. A better fix might be to clip the original pts 221 directly (i.e. do the divide), so we don't spend time subdividing 222 huge lines at all. 223 */ 224 int hx = (x0 >> 1) + (x1 >> 1); 225 int hy = (y0 >> 1) + (y1 >> 1); 226 do_anti_hairline(x0, y0, hx, hy, clip, blitter); 227 do_anti_hairline(hx, hy, x1, y1, clip, blitter); 228 return; 229 } 230 231 int scaleStart, scaleStop; 232 int istart, istop; 233 SkFixed fstart, slope; 234 LineProc proc; 235 236 if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal 237 if (x0 > x1) { // we want to go left-to-right 238 SkTSwap<SkFDot6>(x0, x1); 239 SkTSwap<SkFDot6>(y0, y1); 240 } 241 242 istart = SkFDot6Floor(x0); 243 istop = SkFDot6Ceil(x1); 244 fstart = SkFDot6ToFixed(y0); 245 if (y0 == y1) { // completely horizontal, take fast case 246 slope = 0; 247 proc = hline; 248 } else { 249 slope = fastfixdiv(y1 - y0, x1 - x0); 250 SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1); 251 fstart += (slope * (32 - (x0 & 63)) + 32) >> 6; 252 proc = horish; 253 } 254 255 SkASSERT(istop > istart); 256 if (istop - istart == 1) { 257 scaleStart = x1 - x0; 258 SkASSERT(scaleStart >= 0 && scaleStart <= 64); 259 scaleStop = 0; 260 } else { 261 scaleStart = 64 - (x0 & 63); 262 scaleStop = x1 & 63; 263 } 264 265 if (clip){ 266 if (istart >= clip->fRight || istop <= clip->fLeft) { 267 return; 268 } 269 if (istart < clip->fLeft) { 270 fstart += slope * (clip->fLeft - istart); 271 istart = clip->fLeft; 272 scaleStart = 64; 273 } 274 if (istop > clip->fRight) { 275 istop = clip->fRight; 276 scaleStop = 64; 277 } 278 SkASSERT(istart <= istop); 279 if (istart == istop) { 280 return; 281 } 282 // now test if our Y values are completely inside the clip 283 int top, bottom; 284 if (slope >= 0) { // T2B 285 top = SkFixedFloor(fstart - SK_FixedHalf); 286 bottom = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); 287 } else { // B2T 288 bottom = SkFixedCeil(fstart + SK_FixedHalf); 289 top = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); 290 } 291#ifdef OUTSET_BEFORE_CLIP_TEST 292 top -= 1; 293 bottom += 1; 294#endif 295 if (top >= clip->fBottom || bottom <= clip->fTop) { 296 return; 297 } 298 if (clip->fTop <= top && clip->fBottom >= bottom) { 299 clip = NULL; 300 } 301 } 302 } else { // mostly vertical 303 if (y0 > y1) { // we want to go top-to-bottom 304 SkTSwap<SkFDot6>(x0, x1); 305 SkTSwap<SkFDot6>(y0, y1); 306 } 307 308 istart = SkFDot6Floor(y0); 309 istop = SkFDot6Ceil(y1); 310 fstart = SkFDot6ToFixed(x0); 311 if (x0 == x1) { 312 if (y0 == y1) { // are we zero length? 313 return; // nothing to do 314 } 315 slope = 0; 316 proc = vline; 317 } else { 318 slope = fastfixdiv(x1 - x0, y1 - y0); 319 SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1); 320 fstart += (slope * (32 - (y0 & 63)) + 32) >> 6; 321 proc = vertish; 322 } 323 324 SkASSERT(istop > istart); 325 if (istop - istart == 1) { 326 scaleStart = y1 - y0; 327 SkASSERT(scaleStart >= 0 && scaleStart <= 64); 328 scaleStop = 0; 329 } else { 330 scaleStart = 64 - (y0 & 63); 331 scaleStop = y1 & 63; 332 } 333 334 if (clip) { 335 if (istart >= clip->fBottom || istop <= clip->fTop) { 336 return; 337 } 338 if (istart < clip->fTop) { 339 fstart += slope * (clip->fTop - istart); 340 istart = clip->fTop; 341 scaleStart = 64; 342 } 343 if (istop > clip->fBottom) { 344 istop = clip->fBottom; 345 scaleStop = 64; 346 } 347 SkASSERT(istart <= istop); 348 if (istart == istop) 349 return; 350 351 // now test if our X values are completely inside the clip 352 int left, right; 353 if (slope >= 0) { // L2R 354 left = SkFixedFloor(fstart - SK_FixedHalf); 355 right = SkFixedCeil(fstart + (istop - istart - 1) * slope + SK_FixedHalf); 356 } else { // R2L 357 right = SkFixedCeil(fstart + SK_FixedHalf); 358 left = SkFixedFloor(fstart + (istop - istart - 1) * slope - SK_FixedHalf); 359 } 360#ifdef OUTSET_BEFORE_CLIP_TEST 361 left -= 1; 362 right += 1; 363#endif 364 if (left >= clip->fRight || right <= clip->fLeft) { 365 return; 366 } 367 if (clip->fLeft <= left && clip->fRight >= right) { 368 clip = NULL; 369 } 370 } 371 } 372 373 SkRectClipBlitter rectClipper; 374 if (clip) { 375 rectClipper.init(blitter, *clip); 376 blitter = &rectClipper; 377 } 378 379 fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart); 380 istart += 1; 381 int fullSpans = istop - istart - (scaleStop > 0); 382 if (fullSpans > 0) { 383 fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64); 384 } 385 if (scaleStop > 0) { 386 proc(istop - 1, istop, fstart, slope, blitter, scaleStop); 387 } 388} 389 390void SkScan::AntiHairLineRgn(const SkPoint& pt0, const SkPoint& pt1, 391 const SkRegion* clip, SkBlitter* blitter) { 392 if (clip && clip->isEmpty()) { 393 return; 394 } 395 396 SkASSERT(clip == NULL || !clip->getBounds().isEmpty()); 397 398#ifdef TEST_GAMMA 399 build_gamma_table(); 400#endif 401 402 SkPoint pts[2] = { pt0, pt1 }; 403 404 if (clip) { 405 SkRect clipBounds; 406 clipBounds.set(clip->getBounds()); 407 /* We perform integral clipping later on, but we do a scalar clip first 408 to ensure that our coordinates are expressible in fixed/integers. 409 410 antialiased hairlines can draw up to 1/2 of a pixel outside of 411 their bounds, so we need to outset the clip before calling the 412 clipper. To make the numerics safer, we outset by a whole pixel, 413 since the 1/2 pixel boundary is important to the antihair blitter, 414 we don't want to risk numerical fate by chopping on that edge. 415 */ 416 clipBounds.inset(-SK_Scalar1, -SK_Scalar1); 417 418 if (!SkLineClipper::IntersectLine(pts, clipBounds, pts)) { 419 return; 420 } 421 } 422 423 SkFDot6 x0 = SkScalarToFDot6(pts[0].fX); 424 SkFDot6 y0 = SkScalarToFDot6(pts[0].fY); 425 SkFDot6 x1 = SkScalarToFDot6(pts[1].fX); 426 SkFDot6 y1 = SkScalarToFDot6(pts[1].fY); 427 428 if (clip) { 429 SkFDot6 left = SkMin32(x0, x1); 430 SkFDot6 top = SkMin32(y0, y1); 431 SkFDot6 right = SkMax32(x0, x1); 432 SkFDot6 bottom = SkMax32(y0, y1); 433 SkIRect ir; 434 435 ir.set( SkFDot6Floor(left) - 1, 436 SkFDot6Floor(top) - 1, 437 SkFDot6Ceil(right) + 1, 438 SkFDot6Ceil(bottom) + 1); 439 440 if (clip->quickReject(ir)) { 441 return; 442 } 443 if (!clip->quickContains(ir)) { 444 SkRegion::Cliperator iter(*clip, ir); 445 const SkIRect* r = &iter.rect(); 446 447 while (!iter.done()) { 448 do_anti_hairline(x0, y0, x1, y1, r, blitter); 449 iter.next(); 450 } 451 return; 452 } 453 // fall through to no-clip case 454 } 455 do_anti_hairline(x0, y0, x1, y1, NULL, blitter); 456} 457 458void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip, 459 SkBlitter* blitter) { 460 SkPoint p0, p1; 461 462 p0.set(rect.fLeft, rect.fTop); 463 p1.set(rect.fRight, rect.fTop); 464 SkScan::AntiHairLine(p0, p1, clip, blitter); 465 p0.set(rect.fRight, rect.fBottom); 466 SkScan::AntiHairLine(p0, p1, clip, blitter); 467 p1.set(rect.fLeft, rect.fBottom); 468 SkScan::AntiHairLine(p0, p1, clip, blitter); 469 p0.set(rect.fLeft, rect.fTop); 470 SkScan::AntiHairLine(p0, p1, clip, blitter); 471} 472 473/////////////////////////////////////////////////////////////////////////////// 474 475typedef int FDot8; // 24.8 integer fixed point 476 477static inline FDot8 SkFixedToFDot8(SkFixed x) { 478 return (x + 0x80) >> 8; 479} 480 481static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, 482 SkBlitter* blitter) { 483 SkASSERT(L < R); 484 485 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel 486 blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L)); 487 return; 488 } 489 490 int left = L >> 8; 491 492 if (L & 0xFF) { 493 blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF))); 494 left += 1; 495 } 496 497 int rite = R >> 8; 498 int width = rite - left; 499 if (width > 0) { 500 call_hline_blitter(blitter, left, top, width, alpha); 501 } 502 if (R & 0xFF) { 503 blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF)); 504 } 505} 506 507static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter, 508 bool fillInner) { 509 // check for empty now that we're in our reduced precision space 510 if (L >= R || T >= B) { 511 return; 512 } 513 int top = T >> 8; 514 if (top == ((B - 1) >> 8)) { // just one scanline high 515 do_scanline(L, top, R, B - T - 1, blitter); 516 return; 517 } 518 519 if (T & 0xFF) { 520 do_scanline(L, top, R, 256 - (T & 0xFF), blitter); 521 top += 1; 522 } 523 524 int bot = B >> 8; 525 int height = bot - top; 526 if (height > 0) { 527 int left = L >> 8; 528 if (left == ((R - 1) >> 8)) { // just 1-pixel wide 529 blitter->blitV(left, top, height, R - L - 1); 530 } else { 531 if (L & 0xFF) { 532 blitter->blitV(left, top, height, 256 - (L & 0xFF)); 533 left += 1; 534 } 535 int rite = R >> 8; 536 int width = rite - left; 537 if (width > 0 && fillInner) { 538 blitter->blitRect(left, top, width, height); 539 } 540 if (R & 0xFF) { 541 blitter->blitV(rite, top, height, R & 0xFF); 542 } 543 } 544 } 545 546 if (B & 0xFF) { 547 do_scanline(L, bot, R, B & 0xFF, blitter); 548 } 549} 550 551static void antifillrect(const SkXRect& xr, SkBlitter* blitter) { 552 antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop), 553 SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom), 554 blitter, true); 555} 556 557/////////////////////////////////////////////////////////////////////////////// 558 559void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip, 560 SkBlitter* blitter) { 561 if (NULL == clip) { 562 antifillrect(xr, blitter); 563 } else { 564 SkIRect outerBounds; 565 XRect_roundOut(xr, &outerBounds); 566 567 if (clip->isRect()) { 568 const SkIRect& clipBounds = clip->getBounds(); 569 570 if (clipBounds.contains(outerBounds)) { 571 antifillrect(xr, blitter); 572 } else { 573 SkXRect tmpR; 574 // this keeps our original edges fractional 575 XRect_set(&tmpR, clipBounds); 576 if (tmpR.intersect(xr)) { 577 antifillrect(tmpR, blitter); 578 } 579 } 580 } else { 581 SkRegion::Cliperator clipper(*clip, outerBounds); 582 const SkIRect& rr = clipper.rect(); 583 584 while (!clipper.done()) { 585 SkXRect tmpR; 586 587 // this keeps our original edges fractional 588 XRect_set(&tmpR, rr); 589 if (tmpR.intersect(xr)) { 590 antifillrect(tmpR, blitter); 591 } 592 clipper.next(); 593 } 594 } 595 } 596} 597 598void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip, 599 SkBlitter* blitter) { 600 if (clip.isBW()) { 601 AntiFillXRect(xr, &clip.bwRgn(), blitter); 602 } else { 603 SkIRect outerBounds; 604 XRect_roundOut(xr, &outerBounds); 605 606 if (clip.quickContains(outerBounds)) { 607 AntiFillXRect(xr, NULL, blitter); 608 } else { 609 SkAAClipBlitterWrapper wrapper(clip, blitter); 610 blitter = wrapper.getBlitter(); 611 612 AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter()); 613 } 614 } 615} 616 617#ifdef SK_SCALAR_IS_FLOAT 618 619/* This guy takes a float-rect, but with the key improvement that it has 620 already been clipped, so we know that it is safe to convert it into a 621 XRect (fixedpoint), as it won't overflow. 622*/ 623static void antifillrect(const SkRect& r, SkBlitter* blitter) { 624 SkXRect xr; 625 626 XRect_set(&xr, r); 627 antifillrect(xr, blitter); 628} 629 630/* We repeat the clipping logic of AntiFillXRect because the float rect might 631 overflow if we blindly converted it to an XRect. This sucks that we have to 632 repeat the clipping logic, but I don't see how to share the code/logic. 633 634 We clip r (as needed) into one or more (smaller) float rects, and then pass 635 those to our version of antifillrect, which converts it into an XRect and 636 then calls the blit. 637*/ 638void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip, 639 SkBlitter* blitter) { 640 if (clip) { 641 SkRect newR; 642 newR.set(clip->getBounds()); 643 if (!newR.intersect(origR)) { 644 return; 645 } 646 647 SkIRect outerBounds; 648 newR.roundOut(&outerBounds); 649 650 if (clip->isRect()) { 651 antifillrect(newR, blitter); 652 } else { 653 SkRegion::Cliperator clipper(*clip, outerBounds); 654 while (!clipper.done()) { 655 newR.set(clipper.rect()); 656 if (newR.intersect(origR)) { 657 antifillrect(newR, blitter); 658 } 659 clipper.next(); 660 } 661 } 662 } else { 663 antifillrect(origR, blitter); 664 } 665} 666 667void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip, 668 SkBlitter* blitter) { 669 if (clip.isBW()) { 670 AntiFillRect(r, &clip.bwRgn(), blitter); 671 } else { 672 SkAAClipBlitterWrapper wrap(clip, blitter); 673 AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter()); 674 } 675} 676 677#endif // SK_SCALAR_IS_FLOAT 678 679/////////////////////////////////////////////////////////////////////////////// 680 681#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b) 682 683// calls blitRect() if the rectangle is non-empty 684static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) { 685 if (L < R && T < B) { 686 blitter->blitRect(L, T, R - L, B - T); 687 } 688} 689 690static inline FDot8 SkScalarToFDot8(SkScalar x) { 691#ifdef SK_SCALAR_IS_FLOAT 692 return (int)(x * 256); 693#else 694 return x >> 8; 695#endif 696} 697 698static inline int FDot8Floor(FDot8 x) { 699 return x >> 8; 700} 701 702static inline int FDot8Ceil(FDot8 x) { 703 return (x + 0xFF) >> 8; 704} 705 706// 1 - (1 - a)*(1 - b) 707static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) { 708 // need precise rounding (not just SkAlphaMul) so that values like 709 // a=228, b=252 don't overflow the result 710 return SkToU8(a + b - SkAlphaMulRound(a, b)); 711} 712 713static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha, 714 SkBlitter* blitter) { 715 SkASSERT(L < R); 716 717 if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel 718 blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L)); 719 return; 720 } 721 722 int left = L >> 8; 723 if (L & 0xFF) { 724 blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF)); 725 left += 1; 726 } 727 728 int rite = R >> 8; 729 int width = rite - left; 730 if (width > 0) { 731 call_hline_blitter(blitter, left, top, width, alpha); 732 } 733 734 if (R & 0xFF) { 735 blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF)); 736 } 737} 738 739static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, 740 SkBlitter* blitter) { 741 SkASSERT(L < R && T < B); 742 743 int top = T >> 8; 744 if (top == ((B - 1) >> 8)) { // just one scanline high 745 inner_scanline(L, top, R, B - T, blitter); 746 return; 747 } 748 749 if (T & 0xFF) { 750 inner_scanline(L, top, R, T & 0xFF, blitter); 751 top += 1; 752 } 753 754 int bot = B >> 8; 755 int height = bot - top; 756 if (height > 0) { 757 if (L & 0xFF) { 758 blitter->blitV(L >> 8, top, height, L & 0xFF); 759 } 760 if (R & 0xFF) { 761 blitter->blitV(R >> 8, top, height, ~R & 0xFF); 762 } 763 } 764 765 if (B & 0xFF) { 766 inner_scanline(L, bot, R, ~B & 0xFF, blitter); 767 } 768} 769 770void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 771 const SkRegion* clip, SkBlitter* blitter) { 772 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); 773 774 SkScalar rx = SkScalarHalf(strokeSize.fX); 775 SkScalar ry = SkScalarHalf(strokeSize.fY); 776 777 // outset by the radius 778 FDot8 L = SkScalarToFDot8(r.fLeft - rx); 779 FDot8 T = SkScalarToFDot8(r.fTop - ry); 780 FDot8 R = SkScalarToFDot8(r.fRight + rx); 781 FDot8 B = SkScalarToFDot8(r.fBottom + ry); 782 783 SkIRect outer; 784 // set outer to the outer rect of the outer section 785 outer.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); 786 787 SkBlitterClipper clipper; 788 if (clip) { 789 if (clip->quickReject(outer)) { 790 return; 791 } 792 if (!clip->contains(outer)) { 793 blitter = clipper.apply(blitter, clip, &outer); 794 } 795 // now we can ignore clip for the rest of the function 796 } 797 798 // stroke the outer hull 799 antifilldot8(L, T, R, B, blitter, false); 800 801 // set outer to the outer rect of the middle section 802 outer.set(FDot8Ceil(L), FDot8Ceil(T), FDot8Floor(R), FDot8Floor(B)); 803 804 // in case we lost a bit with diameter/2 805 rx = strokeSize.fX - rx; 806 ry = strokeSize.fY - ry; 807 // inset by the radius 808 L = SkScalarToFDot8(r.fLeft + rx); 809 T = SkScalarToFDot8(r.fTop + ry); 810 R = SkScalarToFDot8(r.fRight - rx); 811 B = SkScalarToFDot8(r.fBottom - ry); 812 813 if (L >= R || T >= B) { 814 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom, 815 blitter); 816 } else { 817 SkIRect inner; 818 // set inner to the inner rect of the middle section 819 inner.set(FDot8Floor(L), FDot8Floor(T), FDot8Ceil(R), FDot8Ceil(B)); 820 821 // draw the frame in 4 pieces 822 fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop, 823 blitter); 824 fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom, 825 blitter); 826 fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom, 827 blitter); 828 fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom, 829 blitter); 830 831 // now stroke the inner rect, which is similar to antifilldot8() except that 832 // it treats the fractional coordinates with the inverse bias (since its 833 // inner). 834 innerstrokedot8(L, T, R, B, blitter); 835 } 836} 837 838void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize, 839 const SkRasterClip& clip, SkBlitter* blitter) { 840 if (clip.isBW()) { 841 AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter); 842 } else { 843 SkAAClipBlitterWrapper wrap(clip, blitter); 844 AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter()); 845 } 846} 847 848