OLD | NEW |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 Loading... |
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 } | |
OLD | NEW |