aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPath.cpp
diff options
context:
space:
mode:
authorGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-10 18:28:12 +0000
committerGravatar bsalomon@google.com <bsalomon@google.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2012-07-10 18:28:12 +0000
commit4eefe6132cbf77696134f65762ebcae574227b77 (patch)
tree16dca787fe3258791f1c8de0c37dd35b6ff276f6 /src/core/SkPath.cpp
parentf0a104e6f16dc095286d32f1e104894ae0b2b19f (diff)
Handle convex paths with degeneracies in cheap direction computation
Review URL: http://codereview.appspot.com/6349085/ git-svn-id: http://skia.googlecode.com/svn/trunk@4515 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'src/core/SkPath.cpp')
-rw-r--r--src/core/SkPath.cpp64
1 files changed, 55 insertions, 9 deletions
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index e2382df497..38619771a3 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -2113,6 +2113,51 @@ static void dumpPath(const SkPath& path) {
}
#endif
+namespace {
+// for use with convex_dir_test
+double mul(double a, double b) { return a * b; }
+SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); }
+double toDouble(SkScalar a) { return SkScalarToDouble(a); }
+SkScalar toScalar(SkScalar a) { return a; }
+
+// determines the winding direction of a convex polygon with the precision
+// of T. CAST_SCALAR casts an SkScalar to T.
+template <typename T, T (CAST_SCALAR)(SkScalar)>
+bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) {
+ // we find the first three points that form a non-degenerate
+ // triangle. If there are no such points then the path is
+ // degenerate. The first is always point 0. Now we find the second
+ // point.
+ int i = 0;
+ enum { kX = 0, kY = 1 };
+ T v0[2];
+ while (1) {
+ v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
+ v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
+ if (v0[kX] || v0[kY]) {
+ break;
+ }
+ if (++i == n - 1) {
+ return false;
+ }
+ }
+ // now find a third point that is not colinear with the first two
+ // points and check the orientation of the triangle (which will be
+ // the same as the orientation of the path).
+ for (++i; i < n; ++i) {
+ T v1[2];
+ v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
+ v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
+ T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]);
+ if (0 != cross) {
+ *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+ return true;
+ }
+ }
+ return false;
+}
+}
+
/*
* We loop through all contours, and keep the computed cross-product of the
* contour that contained the global y-max. If we just look at the first
@@ -2142,15 +2187,16 @@ bool SkPath::cheapComputeDirection(Direction* dir) const {
const SkPoint* pts = iter.pts();
SkScalar cross = 0;
if (kConvex_Convexity == conv) {
- // we loop, skipping over degenerate or flat segments that will
- // return 0 for the cross-product
- for (int i = 0; i < n - 2; ++i) {
- cross = cross_prod(pts[i], pts[i + 1], pts[i + 2]);
- if (cross) {
- // early-exit, as kConvex is assumed to have only 1
- // non-degenerate contour
- return crossToDir(cross, dir);
- }
+ // We try first at scalar precision, and then again at double
+ // precision. This is because the vectors computed between distant
+ // points may lose too much precision.
+ if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) {
+ return true;
+ }
+ if (convex_dir_test<double, toDouble>(n, pts, dir)) {
+ return true;
+ } else {
+ return false;
}
} else {
int index = find_max_y(pts, n);