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