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 644 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
655 SkPathRef::Editor(&fPathRef, inc, inc); | 655 SkPathRef::Editor(&fPathRef, inc, inc); |
656 SkDEBUGCODE(this->validate();) | 656 SkDEBUGCODE(this->validate();) |
657 } | 657 } |
658 | 658 |
659 void SkPath::moveTo(SkScalar x, SkScalar y) { | 659 void SkPath::moveTo(SkScalar x, SkScalar y) { |
660 SkDEBUGCODE(this->validate();) | 660 SkDEBUGCODE(this->validate();) |
661 | 661 |
662 SkPathRef::Editor ed(&fPathRef); | 662 SkPathRef::Editor ed(&fPathRef); |
663 | 663 |
664 // remember our index | 664 // remember our index |
665 fLastMoveToIndex = ed.pathRef()->countPoints(); | 665 fLastMoveToIndex = fPathRef->countPoints(); |
666 | 666 |
667 ed.growForVerb(kMove_Verb)->set(x, y); | 667 ed.growForVerb(kMove_Verb)->set(x, y); |
668 } | 668 } |
669 | 669 |
670 void SkPath::rMoveTo(SkScalar x, SkScalar y) { | 670 void SkPath::rMoveTo(SkScalar x, SkScalar y) { |
671 SkPoint pt; | 671 SkPoint pt; |
672 this->getLastPt(&pt); | 672 this->getLastPt(&pt); |
673 this->moveTo(pt.fX + x, pt.fY + y); | 673 this->moveTo(pt.fX + x, pt.fY + y); |
674 } | 674 } |
675 | 675 |
(...skipping 423 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 { |
1120 this->moveTo(bounds.fLeft, | 1120 this->moveTo(bounds.fLeft, |
1121 bounds.fTop + rrect.fRadii[SkRRect::kUpperLeft_Corner].
fY); | 1121 bounds.fTop + rrect.fRadii[SkRRect::kUpperLeft_Corner].
fY); |
1122 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir); | 1122 add_corner_quads(this, rrect, SkRRect::kLowerLeft_Corner, dir); |
1123 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir); | 1123 add_corner_quads(this, rrect, SkRRect::kLowerRight_Corner, dir); |
1124 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir); | 1124 add_corner_quads(this, rrect, SkRRect::kUpperRight_Corner, dir); |
1125 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir); | 1125 add_corner_quads(this, rrect, SkRRect::kUpperLeft_Corner, dir); |
1126 } | 1126 } |
1127 this->close(); | 1127 this->close(); |
1128 } | 1128 } |
1129 } | 1129 } |
1130 | 1130 |
1131 bool SkPath::hasOnlyMoveTos() const { | 1131 bool SkPath::hasOnlyMoveTos() const { |
1132 int count = fPathRef->countVerbs(); | 1132 int count = fPathRef->countVerbs(); |
1133 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe
mBegin(); | 1133 const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMe
mBegin(); |
1134 for (int i = 0; i < count; ++i) { | 1134 for (int i = 0; i < count; ++i) { |
1135 if (*verbs == kLine_Verb || | 1135 if (*verbs == kLine_Verb || |
1136 *verbs == kQuad_Verb || | 1136 *verbs == kQuad_Verb || |
| 1137 *verbs == kConic_Verb || |
1137 *verbs == kCubic_Verb) { | 1138 *verbs == kCubic_Verb) { |
1138 return false; | 1139 return false; |
1139 } | 1140 } |
1140 ++verbs; | 1141 ++verbs; |
1141 } | 1142 } |
1142 return true; | 1143 return true; |
1143 } | 1144 } |
1144 | 1145 |
1145 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT | 1146 #ifdef SK_IGNORE_QUAD_RR_CORNERS_OPT |
1146 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) | 1147 #define CUBIC_ARC_FACTOR ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3) |
(...skipping 25 matching lines...) Expand all Loading... |
1172 bool skip_vert = ry >= halfH; | 1173 bool skip_vert = ry >= halfH; |
1173 | 1174 |
1174 if (skip_hori && skip_vert) { | 1175 if (skip_hori && skip_vert) { |
1175 this->addOval(rect, dir); | 1176 this->addOval(rect, dir); |
1176 return; | 1177 return; |
1177 } | 1178 } |
1178 | 1179 |
1179 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; | 1180 fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
1180 | 1181 |
1181 SkAutoPathBoundsUpdate apbu(this, rect); | 1182 SkAutoPathBoundsUpdate apbu(this, rect); |
1182 SkAutoDisableDirectionCheck(this); | 1183 SkAutoDisableDirectionCheck addc(this); |
1183 | 1184 |
1184 if (skip_hori) { | 1185 if (skip_hori) { |
1185 rx = halfW; | 1186 rx = halfW; |
1186 } else if (skip_vert) { | 1187 } else if (skip_vert) { |
1187 ry = halfH; | 1188 ry = halfH; |
1188 } | 1189 } |
1189 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); | 1190 SkScalar sx = SkScalarMul(rx, CUBIC_ARC_FACTOR); |
1190 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); | 1191 SkScalar sy = SkScalarMul(ry, CUBIC_ARC_FACTOR); |
1191 | 1192 |
1192 this->incReserve(17); | 1193 this->incReserve(17); |
(...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1504 2, // kConic | 1505 2, // kConic |
1505 3, // kCubic | 1506 3, // kCubic |
1506 0, // kClose | 1507 0, // kClose |
1507 0 // kDone | 1508 0 // kDone |
1508 }; | 1509 }; |
1509 | 1510 |
1510 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb)); | 1511 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb)); |
1511 return gPtsInVerb[verb]; | 1512 return gPtsInVerb[verb]; |
1512 } | 1513 } |
1513 | 1514 |
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 | 1515 // ignore the last point of the 1st contour |
1554 void SkPath::reversePathTo(const SkPath& path) { | 1516 void SkPath::reversePathTo(const SkPath& path) { |
1555 int i, vcount = path.fPathRef->countVerbs(); | 1517 int i, vcount = path.fPathRef->countVerbs(); |
1556 // exit early if the path is empty, or just has a moveTo. | 1518 // exit early if the path is empty, or just has a moveTo. |
1557 if (vcount < 2) { | 1519 if (vcount < 2) { |
1558 return; | 1520 return; |
1559 } | 1521 } |
1560 | 1522 |
1561 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); | 1523 SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); |
1562 | 1524 |
(...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1748 dst->fDirection = kUnknown_Direction; | 1710 dst->fDirection = kUnknown_Direction; |
1749 } else { | 1711 } else { |
1750 SkScalar det2x2 = | 1712 SkScalar det2x2 = |
1751 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix:
:kMScaleY)) - | 1713 SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix:
:kMScaleY)) - |
1752 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::
kMSkewY)); | 1714 SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::
kMSkewY)); |
1753 if (det2x2 < 0) { | 1715 if (det2x2 < 0) { |
1754 dst->fDirection = SkPath::OppositeDirection(static_cast<Directio
n>(fDirection)); | 1716 dst->fDirection = SkPath::OppositeDirection(static_cast<Directio
n>(fDirection)); |
1755 } else if (det2x2 > 0) { | 1717 } else if (det2x2 > 0) { |
1756 dst->fDirection = fDirection; | 1718 dst->fDirection = fDirection; |
1757 } else { | 1719 } else { |
| 1720 dst->fConvexity = kUnknown_Convexity; |
1758 dst->fDirection = kUnknown_Direction; | 1721 dst->fDirection = kUnknown_Direction; |
1759 } | 1722 } |
1760 } | 1723 } |
1761 | 1724 |
1762 // It's an oval only if it stays a rect. | 1725 // It's an oval only if it stays a rect. |
1763 dst->fIsOval = fIsOval && matrix.rectStaysRect(); | 1726 dst->fIsOval = fIsOval && matrix.rectStaysRect(); |
1764 | 1727 |
1765 SkDEBUGCODE(dst->validate();) | 1728 SkDEBUGCODE(dst->validate();) |
1766 } | 1729 } |
1767 } | 1730 } |
(...skipping 544 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2312 #define kValueNeverReturnedBySign 2 | 2275 #define kValueNeverReturnedBySign 2 |
2313 | 2276 |
2314 static bool AlmostEqual(SkScalar compA, SkScalar compB) { | 2277 static bool AlmostEqual(SkScalar compA, SkScalar compB) { |
2315 // The error epsilon was empirically derived; worse case round rects | 2278 // 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 | 2279 // with a mid point outset by 2x float epsilon in tests had an error |
2317 // of 12. | 2280 // of 12. |
2318 const int epsilon = 16; | 2281 const int epsilon = 16; |
2319 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) { | 2282 if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) { |
2320 return false; | 2283 return false; |
2321 } | 2284 } |
2322 if (sk_float_abs(compA) <= FLT_EPSILON && sk_float_abs(compB) <= FLT_EPSILON
) { | 2285 // no need to check for small numbers because SkPath::Iter has removed degen
erate values |
2323 return true; | |
2324 } | |
2325 int aBits = SkFloatAs2sCompliment(compA); | 2286 int aBits = SkFloatAs2sCompliment(compA); |
2326 int bBits = SkFloatAs2sCompliment(compB); | 2287 int bBits = SkFloatAs2sCompliment(compB); |
2327 return aBits < bBits + epsilon && bBits < aBits + epsilon; | 2288 return aBits < bBits + epsilon && bBits < aBits + epsilon; |
2328 } | 2289 } |
2329 | 2290 |
2330 // only valid for a single contour | 2291 // only valid for a single contour |
2331 struct Convexicator { | 2292 struct Convexicator { |
2332 Convexicator() | 2293 Convexicator() |
2333 : fPtCount(0) | 2294 : fPtCount(0) |
2334 , fConvexity(SkPath::kConvex_Convexity) | 2295 , fConvexity(SkPath::kConvex_Convexity) |
(...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2625 } else if (x > max) { | 2586 } else if (x > max) { |
2626 max = x; | 2587 max = x; |
2627 maxIndex = i; | 2588 maxIndex = i; |
2628 } | 2589 } |
2629 } | 2590 } |
2630 *maxIndexPtr = maxIndex; | 2591 *maxIndexPtr = maxIndex; |
2631 return minIndex; | 2592 return minIndex; |
2632 } | 2593 } |
2633 | 2594 |
2634 static void crossToDir(SkScalar cross, SkPath::Direction* dir) { | 2595 static void crossToDir(SkScalar cross, SkPath::Direction* dir) { |
2635 if (dir) { | 2596 *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 } | 2597 } |
2694 | 2598 |
2695 /* | 2599 /* |
2696 * We loop through all contours, and keep the computed cross-product of the | 2600 * 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 | 2601 * 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 | 2602 * 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 | 2603 * 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 | 2604 * that is outer most (or at least has the global y-max) before we can consider |
2701 * its cross product. | 2605 * its cross product. |
2702 */ | 2606 */ |
2703 bool SkPath::cheapComputeDirection(Direction* dir) const { | 2607 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) { | 2608 if (kUnknown_Direction != fDirection) { |
2709 *dir = static_cast<Direction>(fDirection); | 2609 *dir = static_cast<Direction>(fDirection); |
2710 return true; | 2610 return true; |
2711 } | 2611 } |
2712 const Convexity conv = this->getConvexityOrUnknown(); | 2612 |
| 2613 // don't want to pay the cost for computing this if it |
| 2614 // is unknown, so we don't call isConvex() |
| 2615 if (kConvex_Convexity == this->getConvexityOrUnknown()) { |
| 2616 SkASSERT(kUnknown_Direction == fDirection); |
| 2617 *dir = static_cast<Direction>(fDirection); |
| 2618 return false; |
| 2619 } |
2713 | 2620 |
2714 ContourIter iter(*fPathRef.get()); | 2621 ContourIter iter(*fPathRef.get()); |
2715 | 2622 |
2716 // initialize with our logical y-min | 2623 // initialize with our logical y-min |
2717 SkScalar ymax = this->getBounds().fTop; | 2624 SkScalar ymax = this->getBounds().fTop; |
2718 SkScalar ymaxCross = 0; | 2625 SkScalar ymaxCross = 0; |
2719 | 2626 |
2720 for (; !iter.done(); iter.next()) { | 2627 for (; !iter.done(); iter.next()) { |
2721 int n = iter.count(); | 2628 int n = iter.count(); |
2722 if (n < 3) { | 2629 if (n < 3) { |
2723 continue; | 2630 continue; |
2724 } | 2631 } |
2725 | 2632 |
2726 const SkPoint* pts = iter.pts(); | 2633 const SkPoint* pts = iter.pts(); |
2727 SkScalar cross = 0; | 2634 SkScalar cross = 0; |
2728 if (kConvex_Convexity == conv) { | 2635 int index = find_max_y(pts, n); |
2729 // We try first at scalar precision, and then again at double | 2636 if (pts[index].fY < ymax) { |
2730 // precision. This is because the vectors computed between distant | 2637 continue; |
2731 // points may lose too much precision. | 2638 } |
2732 if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) { | 2639 |
2733 fDirection = *dir; | 2640 // If there is more than 1 distinct point at the y-max, we take the |
2734 return true; | 2641 // x-min and x-max of them and just subtract to compute the dir. |
| 2642 if (pts[(index + 1) % n].fY == pts[index].fY) { |
| 2643 int maxIndex; |
| 2644 int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); |
| 2645 if (minIndex == maxIndex) { |
| 2646 goto TRY_CROSSPROD; |
2735 } | 2647 } |
2736 if (convex_dir_test<double, toDouble>(n, pts, dir)) { | 2648 SkASSERT(pts[minIndex].fY == pts[index].fY); |
2737 fDirection = *dir; | 2649 SkASSERT(pts[maxIndex].fY == pts[index].fY); |
2738 return true; | 2650 SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); |
2739 } else { | 2651 // we just subtract the indices, and let that auto-convert to |
2740 return false; | 2652 // SkScalar, since we just want - or + to signal the direction. |
2741 } | 2653 cross = minIndex - maxIndex; |
2742 } else { | 2654 } else { |
2743 int index = find_max_y(pts, n); | 2655 TRY_CROSSPROD: |
2744 if (pts[index].fY < ymax) { | 2656 // Find a next and prev index to use for the cross-product test, |
| 2657 // but we try to find pts that form non-zero vectors from pts[index] |
| 2658 // |
| 2659 // Its possible that we can't find two non-degenerate vectors, so |
| 2660 // we have to guard our search (e.g. all the pts could be in the |
| 2661 // same place). |
| 2662 |
| 2663 // we pass n - 1 instead of -1 so we don't foul up % operator by |
| 2664 // passing it a negative LH argument. |
| 2665 int prev = find_diff_pt(pts, index, n, n - 1); |
| 2666 if (prev == index) { |
| 2667 // completely degenerate, skip to next contour |
2745 continue; | 2668 continue; |
2746 } | 2669 } |
| 2670 int next = find_diff_pt(pts, index, n, 1); |
| 2671 SkASSERT(next != index); |
| 2672 cross = cross_prod(pts[prev], pts[index], pts[next]); |
| 2673 // if we get a zero and the points are horizontal, then we look at t
he spread in |
| 2674 // x-direction. We really should continue to walk away from the dege
neracy until |
| 2675 // there is a divergence. |
| 2676 if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == p
ts[index].fY) { |
| 2677 // construct the subtract so we get the correct Direction below |
| 2678 cross = pts[index].fX - pts[next].fX; |
| 2679 } |
| 2680 } |
2747 | 2681 |
2748 // If there is more than 1 distinct point at the y-max, we take the | 2682 if (cross) { |
2749 // x-min and x-max of them and just subtract to compute the dir. | 2683 // record our best guess so far |
2750 if (pts[(index + 1) % n].fY == pts[index].fY) { | 2684 ymax = pts[index].fY; |
2751 int maxIndex; | 2685 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 } | 2686 } |
2796 } | 2687 } |
2797 if (ymaxCross) { | 2688 if (ymaxCross) { |
2798 crossToDir(ymaxCross, dir); | 2689 crossToDir(ymaxCross, dir); |
2799 fDirection = *dir; | 2690 fDirection = *dir; |
2800 return true; | 2691 return true; |
2801 } else { | 2692 } else { |
2802 return false; | 2693 return false; |
2803 } | 2694 } |
2804 } | 2695 } |
(...skipping 10 matching lines...) Expand all Loading... |
2815 SkScalar A = c3 + 3*(c1 - c2) - c0; | 2706 SkScalar A = c3 + 3*(c1 - c2) - c0; |
2816 SkScalar B = 3*(c2 - c1 - c1 + c0); | 2707 SkScalar B = 3*(c2 - c1 - c1 + c0); |
2817 SkScalar C = 3*(c1 - c0); | 2708 SkScalar C = 3*(c1 - c0); |
2818 SkScalar D = c0; | 2709 SkScalar D = c0; |
2819 return eval_cubic_coeff(A, B, C, D, t); | 2710 return eval_cubic_coeff(A, B, C, D, t); |
2820 } | 2711 } |
2821 | 2712 |
2822 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the | 2713 /* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the |
2823 t value such that cubic(t) = target | 2714 t value such that cubic(t) = target |
2824 */ | 2715 */ |
2825 static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, | 2716 static void chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, |
2826 SkScalar target, SkScalar* t) { | 2717 SkScalar target, SkScalar* t) { |
2827 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); | 2718 // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); |
2828 SkASSERT(c0 < target && target < c3); | 2719 SkASSERT(c0 < target && target < c3); |
2829 | 2720 |
2830 SkScalar D = c0 - target; | 2721 SkScalar D = c0 - target; |
2831 SkScalar A = c3 + 3*(c1 - c2) - c0; | 2722 SkScalar A = c3 + 3*(c1 - c2) - c0; |
2832 SkScalar B = 3*(c2 - c1 - c1 + c0); | 2723 SkScalar B = 3*(c2 - c1 - c1 + c0); |
2833 SkScalar C = 3*(c1 - c0); | 2724 SkScalar C = 3*(c1 - c0); |
2834 | 2725 |
2835 const SkScalar TOLERANCE = SK_Scalar1 / 4096; | 2726 const SkScalar TOLERANCE = SK_Scalar1 / 4096; |
2836 SkScalar minT = 0; | 2727 SkScalar minT = 0; |
2837 SkScalar maxT = SK_Scalar1; | 2728 SkScalar maxT = SK_Scalar1; |
2838 SkScalar mid; | 2729 SkScalar mid; |
2839 int i; | 2730 int i; |
2840 for (i = 0; i < 16; i++) { | 2731 for (i = 0; i < 16; i++) { |
2841 mid = SkScalarAve(minT, maxT); | 2732 mid = SkScalarAve(minT, maxT); |
2842 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); | 2733 SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); |
2843 if (delta < 0) { | 2734 if (delta < 0) { |
2844 minT = mid; | 2735 minT = mid; |
2845 delta = -delta; | 2736 delta = -delta; |
2846 } else { | 2737 } else { |
2847 maxT = mid; | 2738 maxT = mid; |
2848 } | 2739 } |
2849 if (delta < TOLERANCE) { | 2740 if (delta < TOLERANCE) { |
2850 break; | 2741 break; |
2851 } | 2742 } |
2852 } | 2743 } |
2853 *t = mid; | 2744 *t = mid; |
2854 return true; | |
2855 } | 2745 } |
2856 | 2746 |
2857 template <size_t N> static void find_minmax(const SkPoint pts[], | 2747 template <size_t N> static void find_minmax(const SkPoint pts[], |
2858 SkScalar* minPtr, SkScalar* maxPtr)
{ | 2748 SkScalar* minPtr, SkScalar* maxPtr)
{ |
2859 SkScalar min, max; | 2749 SkScalar min, max; |
2860 min = max = pts[0].fX; | 2750 min = max = pts[0].fX; |
2861 for (size_t i = 1; i < N; ++i) { | 2751 for (size_t i = 1; i < N; ++i) { |
2862 min = SkMinScalar(min, pts[i].fX); | 2752 min = SkMinScalar(min, pts[i].fX); |
2863 max = SkMaxScalar(max, pts[i].fX); | 2753 max = SkMaxScalar(max, pts[i].fX); |
2864 } | 2754 } |
(...skipping 21 matching lines...) Expand all Loading... |
2886 SkScalar min, max; | 2776 SkScalar min, max; |
2887 find_minmax<4>(pts, &min, &max); | 2777 find_minmax<4>(pts, &min, &max); |
2888 if (x < min) { | 2778 if (x < min) { |
2889 return 0; | 2779 return 0; |
2890 } | 2780 } |
2891 if (x > max) { | 2781 if (x > max) { |
2892 return dir; | 2782 return dir; |
2893 } | 2783 } |
2894 | 2784 |
2895 // compute the actual x(t) value | 2785 // compute the actual x(t) value |
2896 SkScalar t, xt; | 2786 SkScalar t; |
2897 if (chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t)) { | 2787 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); | 2788 SkScalar 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; | 2789 return xt < x ? dir : 0; |
2904 } | 2790 } |
2905 | 2791 |
2906 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { | 2792 static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { |
2907 SkPoint dst[10]; | 2793 SkPoint dst[10]; |
2908 int n = SkChopCubicAtYExtrema(pts, dst); | 2794 int n = SkChopCubicAtYExtrema(pts, dst); |
2909 int w = 0; | 2795 int w = 0; |
2910 for (int i = 0; i <= n; ++i) { | 2796 for (int i = 0; i <= n; ++i) { |
2911 w += winding_mono_cubic(&dst[i * 3], x, y); | 2797 w += winding_mono_cubic(&dst[i * 3], x, y); |
2912 } | 2798 } |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3052 switch (this->getFillType()) { | 2938 switch (this->getFillType()) { |
3053 case SkPath::kEvenOdd_FillType: | 2939 case SkPath::kEvenOdd_FillType: |
3054 case SkPath::kInverseEvenOdd_FillType: | 2940 case SkPath::kInverseEvenOdd_FillType: |
3055 w &= 1; | 2941 w &= 1; |
3056 break; | 2942 break; |
3057 default: | 2943 default: |
3058 break; | 2944 break; |
3059 } | 2945 } |
3060 return SkToBool(w); | 2946 return SkToBool(w); |
3061 } | 2947 } |
OLD | NEW |