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