OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkScan.h" | 8 #include "SkScan.h" |
9 #include "SkBlitter.h" | 9 #include "SkBlitter.h" |
10 #include "SkRasterClip.h" | 10 #include "SkRasterClip.h" |
(...skipping 330 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
341 than a pixel. | 341 than a pixel. |
342 */ | 342 */ |
343 int level = (33 - SkCLZ(d)) >> 1; | 343 int level = (33 - SkCLZ(d)) >> 1; |
344 // sanity check on level (from the previous version) | 344 // sanity check on level (from the previous version) |
345 if (level > kMaxQuadSubdivideLevel) { | 345 if (level > kMaxQuadSubdivideLevel) { |
346 level = kMaxQuadSubdivideLevel; | 346 level = kMaxQuadSubdivideLevel; |
347 } | 347 } |
348 return level; | 348 return level; |
349 } | 349 } |
350 | 350 |
| 351 static void hairconic(SkPoint pts[4], const SkRegion* clip, SkBlitter* blitter, |
| 352 SkScalar weight, SkScan::HairRgnProc lineproc) {
|
| 353 SkAutoConicToQuads converter; |
| 354 // how close should the quads be to the original conic? |
| 355 const SkScalar tol = SK_Scalar1 / 4; |
| 356 const SkPoint* quadPts = converter.computeQuads(pts, weight, tol); |
| 357 for (int i = 0; i < converter.countQuads(); ++i) { |
| 358 int level = compute_quad_level(quadPts); |
| 359 hairquad(quadPts, clip, blitter, level, lineproc); |
| 360 quadPts += 2; |
| 361 } |
| 362 } |
| 363 |
| 364 /* Extend the points in the direction of the starting or ending tangent by 1/2 u
nit to |
| 365 account for a round or square cap. If there's no distance between the end poi
nt and |
| 366 the control point, use the next control point to create a tangent. If the cur
ve |
| 367 is degenerate, move the cap out 1/2 unit horizontally. */ |
| 368 static void extend_pts(SkPath::Verb prevVerb, SkPath::Verb nextVerb, SkPoint* pt
s, int ptCount) { |
| 369 if (SkPath::kMove_Verb == prevVerb) { |
| 370 SkPoint* first = pts; |
| 371 SkPoint* ctrl = first; |
| 372 int controls = ptCount - 1; |
| 373 SkVector tangent; |
| 374 do { |
| 375 tangent = *first - *++ctrl; |
| 376 } while (tangent.isZero() && --controls > 0); |
| 377 if (tangent.isZero()) { |
| 378 tangent.set(1, 0); |
| 379 } else { |
| 380 tangent.normalize(); |
| 381 } |
| 382 first->fX += tangent.fX * 0.5f; |
| 383 first->fY += tangent.fY * 0.5f; |
| 384 } |
| 385 if (SkPath::kMove_Verb == nextVerb || SkPath::kDone_Verb == nextVerb) { |
| 386 SkPoint* last = &pts[ptCount - 1]; |
| 387 SkPoint* ctrl = last; |
| 388 int controls = ptCount - 1; |
| 389 SkVector tangent; |
| 390 do { |
| 391 tangent = *last - *--ctrl; |
| 392 } while (tangent.isZero() && --controls > 0); |
| 393 if (tangent.isZero()) { |
| 394 tangent.set(-1, 0); |
| 395 } else { |
| 396 tangent.normalize(); |
| 397 } |
| 398 last->fX += tangent.fX * 0.5f; |
| 399 last->fY += tangent.fY * 0.5f; |
| 400 } |
| 401 } |
| 402 |
| 403 enum ExtendEnds { |
| 404 kNo_ExtendEnds, |
| 405 kCaps_ExtendEnds, |
| 406 }; |
| 407 |
351 static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter*
blitter, | 408 static void hair_path(const SkPath& path, const SkRasterClip& rclip, SkBlitter*
blitter, |
352 SkScan::HairRgnProc lineproc) { | 409 SkScan::HairRgnProc lineproc, ExtendEnds extendForCaps) { |
353 if (path.isEmpty()) { | 410 if (path.isEmpty()) { |
354 return; | 411 return; |
355 } | 412 } |
356 | 413 |
357 SkAAClipBlitterWrapper wrap; | 414 SkAAClipBlitterWrapper wrap; |
358 const SkRegion* clip = nullptr; | 415 const SkRegion* clip = nullptr; |
359 | 416 |
360 { | 417 { |
361 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1); | 418 const SkIRect ibounds = path.getBounds().roundOut().makeOutset(1, 1); |
362 | 419 |
363 if (rclip.quickReject(ibounds)) { | 420 if (rclip.quickReject(ibounds)) { |
364 return; | 421 return; |
365 } | 422 } |
366 if (!rclip.quickContains(ibounds)) { | 423 if (!rclip.quickContains(ibounds)) { |
367 if (rclip.isBW()) { | 424 if (rclip.isBW()) { |
368 clip = &rclip.bwRgn(); | 425 clip = &rclip.bwRgn(); |
369 } else { | 426 } else { |
370 wrap.init(rclip, blitter); | 427 wrap.init(rclip, blitter); |
371 blitter = wrap.getBlitter(); | 428 blitter = wrap.getBlitter(); |
372 clip = &wrap.getRgn(); | 429 clip = &wrap.getRgn(); |
373 } | 430 } |
374 } | 431 } |
375 } | 432 } |
376 | 433 |
377 SkPath::Iter iter(path, false); | 434 SkPath::Iter iter(path, false); |
378 SkPoint pts[4]; | 435 SkPoint pts[4]; |
379 SkPath::Verb verb; | 436 SkPath::Verb verb; |
380 SkAutoConicToQuads converter; | |
381 | 437 |
382 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { | 438 if (kNo_ExtendEnds == extendForCaps) { |
383 switch (verb) { | 439 while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) { |
384 case SkPath::kMove_Verb: | 440 switch (verb) { |
| 441 case SkPath::kMove_Verb: |
| 442 break; |
| 443 case SkPath::kLine_Verb: |
| 444 lineproc(pts, 2, clip, blitter); |
| 445 break; |
| 446 case SkPath::kQuad_Verb: |
| 447 hairquad(pts, clip, blitter, compute_quad_level(pts), linepr
oc); |
| 448 break; |
| 449 case SkPath::kConic_Verb: |
| 450 hairconic(pts, clip, blitter, iter.conicWeight(), lineproc); |
| 451 break; |
| 452 case SkPath::kCubic_Verb: |
| 453 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, linep
roc); |
385 break; | 454 break; |
386 case SkPath::kLine_Verb: | 455 case SkPath::kClose_Verb: |
387 lineproc(pts, 2, clip, blitter); | 456 break; |
| 457 case SkPath::kDone_Verb: |
| 458 break; |
| 459 } |
| 460 } |
| 461 } else { |
| 462 /* If the caps require extending the haircurve, use the previous and fol
lowing verbs |
| 463 to determine if the curve is at the start or end of the contour. If t
he curve is |
| 464 preceeded by a move verb, or followed by a move verb or a done verb,
extend the curve. */ |
| 465 SkASSERT(kCaps_ExtendEnds == extendForCaps); |
| 466 SkPath::Verb prevVerb; |
| 467 verb = SkPath::kDone_Verb; |
| 468 SkScalar weight SK_INIT_TO_AVOID_WARNING; |
| 469 do { |
| 470 SkPoint nextPts[4]; |
| 471 SkPath::Verb nextVerb = iter.next(nextPts, false); |
| 472 switch (verb) { |
| 473 case SkPath::kMove_Verb: |
| 474 break; |
| 475 case SkPath::kLine_Verb: |
| 476 extend_pts(prevVerb, nextVerb, pts, 2); |
| 477 lineproc(pts, 2, clip, blitter); |
| 478 break; |
| 479 case SkPath::kQuad_Verb: |
| 480 extend_pts(prevVerb, nextVerb, pts, 3); |
| 481 hairquad(pts, clip, blitter, compute_quad_level(pts), linepr
oc); |
| 482 break; |
| 483 case SkPath::kConic_Verb: |
| 484 extend_pts(prevVerb, nextVerb, pts, 3); |
| 485 hairconic(pts, clip, blitter, weight, lineproc); |
| 486 break; |
| 487 case SkPath::kCubic_Verb: |
| 488 extend_pts(prevVerb, nextVerb, pts, 4); |
| 489 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, linep
roc); |
388 break; | 490 break; |
389 case SkPath::kQuad_Verb: | 491 case SkPath::kClose_Verb: |
390 hairquad(pts, clip, blitter, compute_quad_level(pts), lineproc); | 492 break; |
391 break; | 493 case SkPath::kDone_Verb: |
392 case SkPath::kConic_Verb: { | 494 break; |
393 // how close should the quads be to the original conic? | 495 } |
394 const SkScalar tol = SK_Scalar1 / 4; | 496 if (SkPath::kDone_Verb == nextVerb) { |
395 const SkPoint* quadPts = converter.computeQuads(pts, | |
396 iter.conicWeight(), tol); | |
397 for (int i = 0; i < converter.countQuads(); ++i) { | |
398 int level = compute_quad_level(quadPts); | |
399 hairquad(quadPts, clip, blitter, level, lineproc); | |
400 quadPts += 2; | |
401 } | |
402 break; | 497 break; |
403 } | 498 } |
404 case SkPath::kCubic_Verb: { | 499 if (SkPath::kConic_Verb == nextVerb) { |
405 haircubic(pts, clip, blitter, kMaxCubicSubdivideLevel, lineproc)
; | 500 weight = iter.conicWeight(); |
406 } break; | 501 } |
407 case SkPath::kClose_Verb: | 502 memcpy(pts, nextPts, sizeof(pts)); |
408 break; | 503 prevVerb = verb; |
409 case SkPath::kDone_Verb: | 504 verb = nextVerb; |
410 break; | 505 } while (true); |
411 } | |
412 } | 506 } |
413 } | 507 } |
414 | 508 |
415 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* b
litter) { | 509 void SkScan::HairPath(const SkPath& path, const SkRasterClip& clip, SkBlitter* b
litter) { |
416 hair_path(path, clip, blitter, SkScan::HairLineRgn); | 510 hair_path(path, clip, blitter, SkScan::HairLineRgn, kNo_ExtendEnds); |
417 } | 511 } |
418 | 512 |
419 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitte
r* blitter) { | 513 void SkScan::AntiHairPath(const SkPath& path, const SkRasterClip& clip, SkBlitte
r* blitter) { |
420 hair_path(path, clip, blitter, SkScan::AntiHairLineRgn); | 514 hair_path(path, clip, blitter, SkScan::AntiHairLineRgn, kNo_ExtendEnds); |
| 515 } |
| 516 |
| 517 void SkScan::HairCapPath(const SkPath& path, const SkRasterClip& clip, SkBlitter
* blitter) { |
| 518 hair_path(path, clip, blitter, SkScan::HairLineRgn, kCaps_ExtendEnds); |
| 519 } |
| 520 |
| 521 void SkScan::AntiHairCapPath(const SkPath& path, const SkRasterClip& clip, SkBli
tter* blitter) { |
| 522 hair_path(path, clip, blitter, SkScan::AntiHairLineRgn, kCaps_ExtendEnds); |
421 } | 523 } |
422 | 524 |
423 /////////////////////////////////////////////////////////////////////////////// | 525 /////////////////////////////////////////////////////////////////////////////// |
424 | 526 |
425 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize, | 527 void SkScan::FrameRect(const SkRect& r, const SkPoint& strokeSize, |
426 const SkRasterClip& clip, SkBlitter* blitter) { | 528 const SkRasterClip& clip, SkBlitter* blitter) { |
427 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); | 529 SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0); |
428 | 530 |
429 if (strokeSize.fX < 0 || strokeSize.fY < 0) { | 531 if (strokeSize.fX < 0 || strokeSize.fY < 0) { |
430 return; | 532 return; |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
490 | 592 |
491 SkAAClipBlitterWrapper wrap; | 593 SkAAClipBlitterWrapper wrap; |
492 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { | 594 if (!clip.quickContains(r.roundOut().makeOutset(1, 1))) { |
493 wrap.init(clip, blitter); | 595 wrap.init(clip, blitter); |
494 blitter = wrap.getBlitter(); | 596 blitter = wrap.getBlitter(); |
495 clipRgn = &wrap.getRgn(); | 597 clipRgn = &wrap.getRgn(); |
496 } | 598 } |
497 AntiHairLineRgn(pts, count, clipRgn, blitter); | 599 AntiHairLineRgn(pts, count, clipRgn, blitter); |
498 } | 600 } |
499 } | 601 } |
OLD | NEW |