| 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 |