Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(376)

Side by Side Diff: src/core/SkPath.cpp

Issue 65493004: increase coverage of SkPath.cpp, remove unused code (Closed) Base URL: https://skia.googlecode.com/svn/trunk
Patch Set: remove dead code Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 1
2 /* 2 /*
3 * Copyright 2006 The Android Open Source Project 3 * Copyright 2006 The Android Open Source Project
4 * 4 *
5 * Use of this source code is governed by a BSD-style license that can be 5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file. 6 * found in the LICENSE file.
7 */ 7 */
8 8
9 9
10 #include "SkBuffer.h" 10 #include "SkBuffer.h"
(...skipping 1088 matching lines...) Expand 10 before | Expand all | Expand 10 after
1099 this->addOval(bounds, dir); 1099 this->addOval(bounds, dir);
1100 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT 1100 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT
1101 } else if (rrect.isSimple()) { 1101 } else if (rrect.isSimple()) {
1102 const SkVector& rad = rrect.getSimpleRadii(); 1102 const SkVector& rad = rrect.getSimpleRadii();
1103 this->addRoundRect(bounds, rad.x(), rad.y(), dir); 1103 this->addRoundRect(bounds, rad.x(), rad.y(), dir);
1104 #endif 1104 #endif
1105 } else { 1105 } else {
1106 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; 1106 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
1107 1107
1108 SkAutoPathBoundsUpdate apbu(this, bounds); 1108 SkAutoPathBoundsUpdate apbu(this, bounds);
1109 SkAutoDisableDirectionCheck(this); 1109 SkAutoDisableDirectionCheck addc(this);
1110 1110
1111 this->incReserve(21); 1111 this->incReserve(21);
1112 if (kCW_Direction == dir) { 1112 if (kCW_Direction == dir) {
1113 this->moveTo(bounds.fLeft, 1113 this->moveTo(bounds.fLeft,
1114 bounds.fBottom - rrect.fRadii[SkRRect::kLowerLeft_Corne r].fY); 1114 bounds.fBottom - rrect.fRadii[SkRRect::kLowerLeft_Corne r].fY);
1115 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir); 1115 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir);
1116 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir); 1116 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir);
1117 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir); 1117 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir);
1118 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir); 1118 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir);
1119 } else { 1119 } else {
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
1172 bool skip_vert = ry >= halfH; 1172 bool skip_vert = ry >= halfH;
1173 1173
1174 if (skip_hori && skip_vert) { 1174 if (skip_hori && skip_vert) {
1175 this->addOval(rect, dir); 1175 this->addOval(rect, dir);
1176 return; 1176 return;
1177 } 1177 }
1178 1178
1179 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; 1179 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
1180 1180
1181 SkAutoPathBoundsUpdate apbu(this, rect); 1181 SkAutoPathBoundsUpdate apbu(this, rect);
1182 SkAutoDisableDirectionCheck(this); 1182 SkAutoDisableDirectionCheck addc(this);
1183 1183
1184 if (skip_hori) { 1184 if (skip_hori) {
1185 rx = halfW; 1185 rx = halfW;
1186 } else if (skip_vert) { 1186 } else if (skip_vert) {
1187 ry = halfH; 1187 ry = halfH;
1188 } 1188 }
1189 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); 1189 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
1190 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); 1190 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
1191 1191
1192 this->incReserve(17); 1192 this->incReserve(17);
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after
1504 2, // kConic 1504 2, // kConic
1505 3, // kCubic 1505 3, // kCubic
1506 0, // kClose 1506 0, // kClose
1507 0 // kDone 1507 0 // kDone
1508 }; 1508 };
1509 1509
1510 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb)); 1510 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb));
1511 return gPtsInVerb[verb]; 1511 return gPtsInVerb[verb];
1512 } 1512 }
1513 1513
1514 // ignore the initial moveto, and stop when the 1st contour ends
1515 void SkPath::pathTo(const SkPath& path) {
1516 int i, vcount = path.fPathRef->countVerbs();
1517 // exit early if the path is empty, or just has a moveTo.
1518 if (vcount < 2) {
1519 return;
1520 }
1521
1522 SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
1523
1524 fIsOval = false;
1525
1526 const uint8_t* verbs = path.fPathRef->verbs();
1527 // skip the initial moveTo
1528 const SkPoint* pts = path.fPathRef->points() + 1;
1529 const SkScalar* conicWeight = path.fPathRef->conicWeights();
1530
1531 SkASSERT(verbs[~0] == kMove_Verb);
1532 for (i = 1; i < vcount; i++) {
1533 switch (verbs[~i]) {
1534 case kLine_Verb:
1535 this->lineTo(pts[0].fX, pts[0].fY);
1536 break;
1537 case kQuad_Verb:
1538 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
1539 break;
1540 case kConic_Verb:
1541 this->conicTo(pts[0], pts[1], *conicWeight++);
1542 break;
1543 case kCubic_Verb:
1544 this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2] .fX, pts[2].fY);
1545 break;
1546 case kClose_Verb:
1547 return;
1548 }
1549 pts += pts_in_verb(verbs[~i]);
1550 }
1551 }
1552
1553 // ignore the last point of the 1st contour 1514 // ignore the last point of the 1st contour
1554 void SkPath::reversePathTo(const SkPath& path) { 1515 void SkPath::reversePathTo(const SkPath& path) {
1555 int i, vcount = path.fPathRef->countVerbs(); 1516 int i, vcount = path.fPathRef->countVerbs();
1556 // exit early if the path is empty, or just has a moveTo. 1517 // exit early if the path is empty, or just has a moveTo.
1557 if (vcount < 2) { 1518 if (vcount < 2) {
1558 return; 1519 return;
1559 } 1520 }
1560 1521
1561 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); 1522 SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
1562 1523
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after
1748 dst->fDirection = kUnknown_Direction; 1709 dst->fDirection = kUnknown_Direction;
1749 } else { 1710 } else {
1750 SkScalar det2x2 = 1711 SkScalar det2x2 =
1751 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix: :kMScaleY)) - 1712 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix: :kMScaleY)) -
1752 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix:: kMSkewY)); 1713 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix:: kMSkewY));
1753 if (det2x2 < 0) { 1714 if (det2x2 < 0) {
1754 dst->fDirection = SkPath::OppositeDirection(static_cast<Directio n>(fDirection)); 1715 dst->fDirection = SkPath::OppositeDirection(static_cast<Directio n>(fDirection));
1755 } else if (det2x2 > 0) { 1716 } else if (det2x2 > 0) {
1756 dst->fDirection = fDirection; 1717 dst->fDirection = fDirection;
1757 } else { 1718 } else {
1719 dst->fConvexity = kUnknown_Convexity;
1758 dst->fDirection = kUnknown_Direction; 1720 dst->fDirection = kUnknown_Direction;
1759 } 1721 }
1760 } 1722 }
1761 1723
1762 // It's an oval only if it stays a rect. 1724 // It's an oval only if it stays a rect.
1763 dst->fIsOval = fIsOval && matrix.rectStaysRect(); 1725 dst->fIsOval = fIsOval && matrix.rectStaysRect();
1764 1726
1765 SkDEBUGCODE(dst->validate();) 1727 SkDEBUGCODE(dst->validate();)
1766 } 1728 }
1767 } 1729 }
(...skipping 544 matching lines...) Expand 10 before | Expand all | Expand 10 after
2312 #define kValueNeverReturnedBySign 2 2274 #define kValueNeverReturnedBySign 2
2313 2275
2314 static bool AlmostEqual(SkScalar compA, SkScalar compB) { 2276 static bool AlmostEqual(SkScalar compA, SkScalar compB) {
2315 // The error epsilon was empirically derived; worse case round rects 2277 // The error epsilon was empirically derived; worse case round rects
2316 // with a mid point outset by 2x float epsilon in tests had an error 2278 // with a mid point outset by 2x float epsilon in tests had an error
2317 // of 12. 2279 // of 12.
2318 const int epsilon = 16; 2280 const int epsilon = 16;
2319 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) { 2281 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) {
2320 return false; 2282 return false;
2321 } 2283 }
2322 if (sk_float_abs(compA) <= FLT_EPSILON && sk_float_abs(compB) <= FLT_EPSILON ) { 2284 // no need to check for small numbers because SkPath::Iter has removed degen erate values
2323 return true;
2324 }
2325 int aBits = SkFloatAs2sCompliment(compA); 2285 int aBits = SkFloatAs2sCompliment(compA);
2326 int bBits = SkFloatAs2sCompliment(compB); 2286 int bBits = SkFloatAs2sCompliment(compB);
2327 return aBits < bBits + epsilon && bBits < aBits + epsilon; 2287 return aBits < bBits + epsilon && bBits < aBits + epsilon;
2328 } 2288 }
2329 2289
2330 // only valid for a single contour 2290 // only valid for a single contour
2331 struct Convexicator { 2291 struct Convexicator {
2332 Convexicator() 2292 Convexicator()
2333 : fPtCount(0) 2293 : fPtCount(0)
2334 , fConvexity(SkPath::kConvex_Convexity) 2294 , fConvexity(SkPath::kConvex_Convexity)
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after
2625 } else if (x > max) { 2585 } else if (x > max) {
2626 max = x; 2586 max = x;
2627 maxIndex = i; 2587 maxIndex = i;
2628 } 2588 }
2629 } 2589 }
2630 *maxIndexPtr = maxIndex; 2590 *maxIndexPtr = maxIndex;
2631 return minIndex; 2591 return minIndex;
2632 } 2592 }
2633 2593
2634 static void crossToDir(SkScalar cross, SkPath::Direction* dir) { 2594 static void crossToDir(SkScalar cross, SkPath::Direction* dir) {
2635 if (dir) { 2595 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
2636 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
2637 }
2638 }
2639
2640 #if 0
2641 #include "SkString.h"
2642 #include "../utils/SkParsePath.h"
2643 static void dumpPath(const SkPath& path) {
2644 SkString str;
2645 SkParsePath::ToSVGString(path, &str);
2646 SkDebugf("%s\n", str.c_str());
2647 }
2648 #endif
2649
2650 namespace {
2651 // for use with convex_dir_test
2652 double mul(double a, double b) { return a * b; }
2653 SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); }
2654 double toDouble(SkScalar a) { return SkScalarToDouble(a); }
2655 SkScalar toScalar(SkScalar a) { return a; }
2656
2657 // determines the winding direction of a convex polygon with the precision
2658 // of T. CAST_SCALAR casts an SkScalar to T.
2659 template <typename T, T (CAST_SCALAR)(SkScalar)>
2660 bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) {
2661 // we find the first three points that form a non-degenerate
2662 // triangle. If there are no such points then the path is
2663 // degenerate. The first is always point 0. Now we find the second
2664 // point.
2665 int i = 0;
2666 enum { kX = 0, kY = 1 };
2667 T v0[2];
2668 while (1) {
2669 v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
2670 v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
2671 if (v0[kX] || v0[kY]) {
2672 break;
2673 }
2674 if (++i == n - 1) {
2675 return false;
2676 }
2677 }
2678 // now find a third point that is not colinear with the first two
2679 // points and check the orientation of the triangle (which will be
2680 // the same as the orientation of the path).
2681 for (++i; i < n; ++i) {
2682 T v1[2];
2683 v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
2684 v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
2685 T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]);
2686 if (0 != cross) {
2687 *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
2688 return true;
2689 }
2690 }
2691 return false;
2692 }
2693 } 2596 }
2694 2597
2695 /* 2598 /*
2696 * We loop through all contours, and keep the computed cross-product of the 2599 * We loop through all contours, and keep the computed cross-product of the
2697 * contour that contained the global y-max. If we just look at the first 2600 * contour that contained the global y-max. If we just look at the first
2698 * contour, we may find one that is wound the opposite way (correctly) since 2601 * contour, we may find one that is wound the opposite way (correctly) since
2699 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour 2602 * it is the interior of a hole (e.g. 'o'). Thus we must find the contour
2700 * that is outer most (or at least has the global y-max) before we can consider 2603 * that is outer most (or at least has the global y-max) before we can consider
2701 * its cross product. 2604 * its cross product.
2702 */ 2605 */
2703 bool SkPath::cheapComputeDirection(Direction* dir) const { 2606 bool SkPath::cheapComputeDirection(Direction* dir) const {
2704 // dumpPath(*this);
2705 // don't want to pay the cost for computing this if it
2706 // is unknown, so we don't call isConvex()
2707
2708 if (kUnknown_Direction != fDirection) { 2607 if (kUnknown_Direction != fDirection) {
2709 *dir = static_cast<Direction>(fDirection); 2608 *dir = static_cast<Direction>(fDirection);
2710 return true; 2609 return true;
2711 } 2610 }
2712 const Convexity conv = this->getConvexityOrUnknown(); 2611
2612 // don't want to pay the cost for computing this if it
2613 // is unknown, so we don't call isConvex()
2614 if (kConvex_Convexity == this->getConvexityOrUnknown()) {
2615 SkASSERT(kUnknown_Direction == fDirection);
2616 *dir = static_cast<Direction>(fDirection);
2617 return false;
2618 }
2713 2619
2714 ContourIter iter(*fPathRef.get()); 2620 ContourIter iter(*fPathRef.get());
2715 2621
2716 // initialize with our logical y-min 2622 // initialize with our logical y-min
2717 SkScalar ymax = this->getBounds().fTop; 2623 SkScalar ymax = this->getBounds().fTop;
2718 SkScalar ymaxCross = 0; 2624 SkScalar ymaxCross = 0;
2719 2625
2720 for (; !iter.done(); iter.next()) { 2626 for (; !iter.done(); iter.next()) {
2721 int n = iter.count(); 2627 int n = iter.count();
2722 if (n < 3) { 2628 if (n < 3) {
2723 continue; 2629 continue;
2724 } 2630 }
2725 2631
2726 const SkPoint* pts = iter.pts(); 2632 const SkPoint* pts = iter.pts();
2727 SkScalar cross = 0; 2633 SkScalar cross = 0;
2728 if (kConvex_Convexity == conv) { 2634 int index = find_max_y(pts, n);
2729 // We try first at scalar precision, and then again at double 2635 if (pts[index].fY < ymax) {
2730 // precision. This is because the vectors computed between distant 2636 continue;
2731 // points may lose too much precision. 2637 }
2732 if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) { 2638
2733 fDirection = *dir; 2639 // If there is more than 1 distinct point at the y-max, we take the
2734 return true; 2640 // x-min and x-max of them and just subtract to compute the dir.
2641 if (pts[(index + 1) % n].fY == pts[index].fY) {
2642 int maxIndex;
2643 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2644 if (minIndex == maxIndex) {
2645 goto TRY_CROSSPROD;
2735 } 2646 }
2736 if (convex_dir_test<double, toDouble>(n, pts, dir)) { 2647 SkASSERT(pts[minIndex].fY == pts[index].fY);
2737 fDirection = *dir; 2648 SkASSERT(pts[maxIndex].fY == pts[index].fY);
2738 return true; 2649 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2739 } else { 2650 // we just subtract the indices, and let that auto-convert to
2740 return false; 2651 // SkScalar, since we just want - or + to signal the direction.
2741 } 2652 cross = minIndex - maxIndex;
2742 } else { 2653 } else {
2743 int index = find_max_y(pts, n); 2654 TRY_CROSSPROD:
2744 if (pts[index].fY < ymax) { 2655 // Find a next and prev index to use for the cross-product test,
2656 // but we try to find pts that form non-zero vectors from pts[index]
2657 //
2658 // Its possible that we can't find two non-degenerate vectors, so
2659 // we have to guard our search (e.g. all the pts could be in the
2660 // same place).
2661
2662 // we pass n - 1 instead of -1 so we don't foul up % operator by
2663 // passing it a negative LH argument.
2664 int prev = find_diff_pt(pts, index, n, n - 1);
2665 if (prev == index) {
2666 // completely degenerate, skip to next contour
2745 continue; 2667 continue;
2746 } 2668 }
2669 int next = find_diff_pt(pts, index, n, 1);
2670 SkASSERT(next != index);
2671 cross = cross_prod(pts[prev], pts[index], pts[next]);
2672 // if we get a zero and the points are horizontal, then we look at t he spread in
2673 // x-direction. We really should continue to walk away from the dege neracy until
2674 // there is a divergence.
2675 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == p ts[index].fY) {
2676 // construct the subtract so we get the correct Direction below
2677 cross = pts[index].fX - pts[next].fX;
2678 }
2679 }
2747 2680
2748 // If there is more than 1 distinct point at the y-max, we take the 2681 if (cross) {
2749 // x-min and x-max of them and just subtract to compute the dir. 2682 // record our best guess so far
2750 if (pts[(index + 1) % n].fY == pts[index].fY) { 2683 ymax = pts[index].fY;
2751 int maxIndex; 2684 ymaxCross = cross;
2752 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex);
2753 if (minIndex == maxIndex) {
2754 goto TRY_CROSSPROD;
2755 }
2756 SkASSERT(pts[minIndex].fY == pts[index].fY);
2757 SkASSERT(pts[maxIndex].fY == pts[index].fY);
2758 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX);
2759 // we just subtract the indices, and let that auto-convert to
2760 // SkScalar, since we just want - or + to signal the direction.
2761 cross = minIndex - maxIndex;
2762 } else {
2763 TRY_CROSSPROD:
2764 // Find a next and prev index to use for the cross-product test,
2765 // but we try to find pts that form non-zero vectors from pts[in dex]
2766 //
2767 // Its possible that we can't find two non-degenerate vectors, s o
2768 // we have to guard our search (e.g. all the pts could be in the
2769 // same place).
2770
2771 // we pass n - 1 instead of -1 so we don't foul up % operator by
2772 // passing it a negative LH argument.
2773 int prev = find_diff_pt(pts, index, n, n - 1);
2774 if (prev == index) {
2775 // completely degenerate, skip to next contour
2776 continue;
2777 }
2778 int next = find_diff_pt(pts, index, n, 1);
2779 SkASSERT(next != index);
2780 cross = cross_prod(pts[prev], pts[index], pts[next]);
2781 // if we get a zero and the points are horizontal, then we look at the spread in
2782 // x-direction. We really should continue to walk away from the degeneracy until
2783 // there is a divergence.
2784 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
2785 // construct the subtract so we get the correct Direction be low
2786 cross = pts[index].fX - pts[next].fX;
2787 }
2788 }
2789
2790 if (cross) {
2791 // record our best guess so far
2792 ymax = pts[index].fY;
2793 ymaxCross = cross;
2794 }
2795 } 2685 }
2796 } 2686 }
2797 if (ymaxCross) { 2687 if (ymaxCross) {
2798 crossToDir(ymaxCross, dir); 2688 crossToDir(ymaxCross, dir);
2799 fDirection = *dir; 2689 fDirection = *dir;
2800 return true; 2690 return true;
2801 } else { 2691 } else {
2802 return false; 2692 return false;
2803 } 2693 }
2804 } 2694 }
2805 2695
2806 ///////////////////////////////////////////////////////////////////////////////
2807
2808 static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C,
2809 SkScalar D, SkScalar t) {
2810 return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D);
2811 }
2812
2813 static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c 3,
2814 SkScalar t) {
2815 SkScalar A = c3 + 3*(c1 - c2) - c0;
2816 SkScalar B = 3*(c2 - c1 - c1 + c0);
2817 SkScalar C = 3*(c1 - c0);
2818 SkScalar D = c0;
2819 return eval_cubic_coeff(A, B, C, D, t);
2820 }
2821
2822 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the
2823 t value such that cubic(t) = target
2824 */
2825 static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
2826 SkScalar target, SkScalar* t) {
2827 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3);
2828 SkASSERT(c0 < target && target < c3);
2829
2830 SkScalar D = c0 - target;
2831 SkScalar A = c3 + 3*(c1 - c2) - c0;
2832 SkScalar B = 3*(c2 - c1 - c1 + c0);
2833 SkScalar C = 3*(c1 - c0);
2834
2835 const SkScalar TOLERANCE = SK_Scalar1 / 4096;
2836 SkScalar minT = 0;
2837 SkScalar maxT = SK_Scalar1;
2838 SkScalar mid;
2839 int i;
2840 for (i = 0; i < 16; i++) {
2841 mid = SkScalarAve(minT, maxT);
2842 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
2843 if (delta < 0) {
2844 minT = mid;
2845 delta = -delta;
2846 } else {
2847 maxT = mid;
2848 }
2849 if (delta < TOLERANCE) {
2850 break;
2851 }
2852 }
2853 *t = mid;
2854 return true;
2855 }
2856
2857 template <size_t N> static void find_minmax(const SkPoint pts[],
2858 SkScalar* minPtr, SkScalar* maxPtr) {
2859 SkScalar min, max;
2860 min = max = pts[0].fX;
2861 for (size_t i = 1; i < N; ++i) {
2862 min = SkMinScalar(min, pts[i].fX);
2863 max = SkMaxScalar(max, pts[i].fX);
2864 }
2865 *minPtr = min;
2866 *maxPtr = max;
2867 }
2868
2869 static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
2870 SkPoint storage[4];
2871
2872 int dir = 1;
2873 if (pts[0].fY > pts[3].fY) {
2874 storage[0] = pts[3];
2875 storage[1] = pts[2];
2876 storage[2] = pts[1];
2877 storage[3] = pts[0];
2878 pts = storage;
2879 dir = -1;
2880 }
2881 if (y < pts[0].fY || y >= pts[3].fY) {
2882 return 0;
2883 }
2884
2885 // quickreject or quickaccept
2886 SkScalar min, max;
2887 find_minmax<4>(pts, &min, &max);
2888 if (x < min) {
2889 return 0;
2890 }
2891 if (x > max) {
2892 return dir;
2893 }
2894
2895 // compute the actual x(t) value
2896 SkScalar t, xt;
2897 if (chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t)) {
2898 xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
2899 } else {
2900 SkScalar mid = SkScalarAve(pts[0].fY, pts[3].fY);
2901 xt = y < mid ? pts[0].fX : pts[3].fX;
2902 }
2903 return xt < x ? dir : 0;
2904 }
2905
2906 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
2907 SkPoint dst[10];
2908 int n = SkChopCubicAtYExtrema(pts, dst);
2909 int w = 0;
2910 for (int i = 0; i <= n; ++i) {
2911 w += winding_mono_cubic(&dst[i * 3], x, y);
2912 }
2913 return w;
2914 }
2915
2916 static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
2917 SkScalar y0 = pts[0].fY;
2918 SkScalar y2 = pts[2].fY;
2919
2920 int dir = 1;
2921 if (y0 > y2) {
2922 SkTSwap(y0, y2);
2923 dir = -1;
2924 }
2925 if (y < y0 || y >= y2) {
2926 return 0;
2927 }
2928
2929 // bounds check on X (not required. is it faster?)
2930 #if 0
2931 if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
2932 return 0;
2933 }
2934 #endif
2935
2936 SkScalar roots[2];
2937 int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
2938 2 * (pts[1].fY - pts[0].fY),
2939 pts[0].fY - y,
2940 roots);
2941 SkASSERT(n <= 1);
2942 SkScalar xt;
2943 if (0 == n) {
2944 SkScalar mid = SkScalarAve(y0, y2);
2945 // Need [0] and [2] if dir == 1
2946 // and [2] and [0] if dir == -1
2947 xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX;
2948 } else {
2949 SkScalar t = roots[0];
2950 SkScalar C = pts[0].fX;
2951 SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
2952 SkScalar B = 2 * (pts[1].fX - C);
2953 xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
2954 }
2955 return xt < x ? dir : 0;
2956 }
2957
2958 static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
2959 // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
2960 if (y0 == y1) {
2961 return true;
2962 }
2963 if (y0 < y1) {
2964 return y1 <= y2;
2965 } else {
2966 return y1 >= y2;
2967 }
2968 }
2969
2970 static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
2971 SkPoint dst[5];
2972 int n = 0;
2973
2974 if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
2975 n = SkChopQuadAtYExtrema(pts, dst);
2976 pts = dst;
2977 }
2978 int w = winding_mono_quad(pts, x, y);
2979 if (n > 0) {
2980 w += winding_mono_quad(&pts[2], x, y);
2981 }
2982 return w;
2983 }
2984
2985 static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) {
2986 SkScalar x0 = pts[0].fX;
2987 SkScalar y0 = pts[0].fY;
2988 SkScalar x1 = pts[1].fX;
2989 SkScalar y1 = pts[1].fY;
2990
2991 SkScalar dy = y1 - y0;
2992
2993 int dir = 1;
2994 if (y0 > y1) {
2995 SkTSwap(y0, y1);
2996 dir = -1;
2997 }
2998 if (y < y0 || y >= y1) {
2999 return 0;
3000 }
3001
3002 SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) -
3003 SkScalarMul(dy, x - pts[0].fX);
3004
3005 if (SkScalarSignAsInt(cross) == dir) {
3006 dir = 0;
3007 }
3008 return dir;
3009 }
3010
3011 static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) {
3012 return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom;
3013 }
3014
3015 bool SkPath::contains(SkScalar x, SkScalar y) const {
3016 bool isInverse = this->isInverseFillType();
3017 if (this->isEmpty()) {
3018 return isInverse;
3019 }
3020
3021 if (!contains_inclusive(this->getBounds(), x, y)) {
3022 return isInverse;
3023 }
3024
3025 SkPath::Iter iter(*this, true);
3026 bool done = false;
3027 int w = 0;
3028 do {
3029 SkPoint pts[4];
3030 switch (iter.next(pts, false)) {
3031 case SkPath::kMove_Verb:
3032 case SkPath::kClose_Verb:
3033 break;
3034 case SkPath::kLine_Verb:
3035 w += winding_line(pts, x, y);
3036 break;
3037 case SkPath::kQuad_Verb:
3038 w += winding_quad(pts, x, y);
3039 break;
3040 case SkPath::kConic_Verb:
3041 SkASSERT(0);
3042 break;
3043 case SkPath::kCubic_Verb:
3044 w += winding_cubic(pts, x, y);
3045 break;
3046 case SkPath::kDone_Verb:
3047 done = true;
3048 break;
3049 }
3050 } while (!done);
3051
3052 switch (this->getFillType()) {
3053 case SkPath::kEvenOdd_FillType:
3054 case SkPath::kInverseEvenOdd_FillType:
3055 w &= 1;
3056 break;
3057 default:
3058 break;
3059 }
3060 return SkToBool(w);
3061 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698