Index: src/core/SkPath.cpp |
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp |
index f772717e2fba99525fa15e936c71465241646f0f..882d1b750796e8b98b372b05f4b7f1354cdaec2b 100644 |
--- a/src/core/SkPath.cpp |
+++ b/src/core/SkPath.cpp |
@@ -1106,7 +1106,7 @@ void SkPath::addRRect(const SkRRect& rrect, Direction dir) { |
fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
SkAutoPathBoundsUpdate apbu(this, bounds); |
- SkAutoDisableDirectionCheck(this); |
+ SkAutoDisableDirectionCheck addc(this); |
this->incReserve(21); |
if (kCW_Direction == dir) { |
@@ -1179,7 +1179,7 @@ void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, |
fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction; |
SkAutoPathBoundsUpdate apbu(this, rect); |
- SkAutoDisableDirectionCheck(this); |
+ SkAutoDisableDirectionCheck addc(this); |
if (skip_hori) { |
rx = halfW; |
@@ -1511,45 +1511,6 @@ static int pts_in_verb(unsigned verb) { |
return gPtsInVerb[verb]; |
} |
-// ignore the initial moveto, and stop when the 1st contour ends |
-void SkPath::pathTo(const SkPath& path) { |
- int i, vcount = path.fPathRef->countVerbs(); |
- // exit early if the path is empty, or just has a moveTo. |
- if (vcount < 2) { |
- return; |
- } |
- |
- SkPathRef::Editor(&fPathRef, vcount, path.countPoints()); |
- |
- fIsOval = false; |
- |
- const uint8_t* verbs = path.fPathRef->verbs(); |
- // skip the initial moveTo |
- const SkPoint* pts = path.fPathRef->points() + 1; |
- const SkScalar* conicWeight = path.fPathRef->conicWeights(); |
- |
- SkASSERT(verbs[~0] == kMove_Verb); |
- for (i = 1; i < vcount; i++) { |
- switch (verbs[~i]) { |
- case kLine_Verb: |
- this->lineTo(pts[0].fX, pts[0].fY); |
- break; |
- case kQuad_Verb: |
- this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY); |
- break; |
- case kConic_Verb: |
- this->conicTo(pts[0], pts[1], *conicWeight++); |
- break; |
- case kCubic_Verb: |
- this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY); |
- break; |
- case kClose_Verb: |
- return; |
- } |
- pts += pts_in_verb(verbs[~i]); |
- } |
-} |
- |
// ignore the last point of the 1st contour |
void SkPath::reversePathTo(const SkPath& path) { |
int i, vcount = path.fPathRef->countVerbs(); |
@@ -1755,6 +1716,7 @@ void SkPath::transform(const SkMatrix& matrix, SkPath* dst) const { |
} else if (det2x2 > 0) { |
dst->fDirection = fDirection; |
} else { |
+ dst->fConvexity = kUnknown_Convexity; |
dst->fDirection = kUnknown_Direction; |
} |
} |
@@ -2319,9 +2281,7 @@ static bool AlmostEqual(SkScalar compA, SkScalar compB) { |
if (!SkScalarIsFinite(compA) || !SkScalarIsFinite(compB)) { |
return false; |
} |
- if (sk_float_abs(compA) <= FLT_EPSILON && sk_float_abs(compB) <= FLT_EPSILON) { |
- return true; |
- } |
+ // no need to check for small numbers because SkPath::Iter has removed degenerate values |
int aBits = SkFloatAs2sCompliment(compA); |
int bBits = SkFloatAs2sCompliment(compB); |
return aBits < bBits + epsilon && bBits < aBits + epsilon; |
@@ -2632,64 +2592,7 @@ static int find_min_max_x_at_y(const SkPoint pts[], int index, int n, |
} |
static void crossToDir(SkScalar cross, SkPath::Direction* dir) { |
- if (dir) { |
- *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; |
- } |
-} |
- |
-#if 0 |
-#include "SkString.h" |
-#include "../utils/SkParsePath.h" |
-static void dumpPath(const SkPath& path) { |
- SkString str; |
- SkParsePath::ToSVGString(path, &str); |
- SkDebugf("%s\n", str.c_str()); |
-} |
-#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; |
-} |
+ *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction; |
} |
/* |
@@ -2701,15 +2604,18 @@ bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) { |
* its cross product. |
*/ |
bool SkPath::cheapComputeDirection(Direction* dir) const { |
-// dumpPath(*this); |
- // don't want to pay the cost for computing this if it |
- // is unknown, so we don't call isConvex() |
- |
if (kUnknown_Direction != fDirection) { |
*dir = static_cast<Direction>(fDirection); |
return true; |
} |
- const Convexity conv = this->getConvexityOrUnknown(); |
+ |
+ // don't want to pay the cost for computing this if it |
+ // is unknown, so we don't call isConvex() |
+ if (kConvex_Convexity == this->getConvexityOrUnknown()) { |
+ SkASSERT(kUnknown_Direction == fDirection); |
+ *dir = static_cast<Direction>(fDirection); |
+ return false; |
+ } |
ContourIter iter(*fPathRef.get()); |
@@ -2725,73 +2631,57 @@ bool SkPath::cheapComputeDirection(Direction* dir) const { |
const SkPoint* pts = iter.pts(); |
SkScalar cross = 0; |
- if (kConvex_Convexity == conv) { |
- // 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)) { |
- fDirection = *dir; |
- return true; |
- } |
- if (convex_dir_test<double, toDouble>(n, pts, dir)) { |
- fDirection = *dir; |
- return true; |
- } else { |
- return false; |
+ int index = find_max_y(pts, n); |
+ if (pts[index].fY < ymax) { |
+ continue; |
+ } |
+ |
+ // If there is more than 1 distinct point at the y-max, we take the |
+ // x-min and x-max of them and just subtract to compute the dir. |
+ if (pts[(index + 1) % n].fY == pts[index].fY) { |
+ int maxIndex; |
+ int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); |
+ if (minIndex == maxIndex) { |
+ goto TRY_CROSSPROD; |
} |
+ SkASSERT(pts[minIndex].fY == pts[index].fY); |
+ SkASSERT(pts[maxIndex].fY == pts[index].fY); |
+ SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); |
+ // we just subtract the indices, and let that auto-convert to |
+ // SkScalar, since we just want - or + to signal the direction. |
+ cross = minIndex - maxIndex; |
} else { |
- int index = find_max_y(pts, n); |
- if (pts[index].fY < ymax) { |
+ TRY_CROSSPROD: |
+ // Find a next and prev index to use for the cross-product test, |
+ // but we try to find pts that form non-zero vectors from pts[index] |
+ // |
+ // Its possible that we can't find two non-degenerate vectors, so |
+ // we have to guard our search (e.g. all the pts could be in the |
+ // same place). |
+ |
+ // we pass n - 1 instead of -1 so we don't foul up % operator by |
+ // passing it a negative LH argument. |
+ int prev = find_diff_pt(pts, index, n, n - 1); |
+ if (prev == index) { |
+ // completely degenerate, skip to next contour |
continue; |
} |
- |
- // If there is more than 1 distinct point at the y-max, we take the |
- // x-min and x-max of them and just subtract to compute the dir. |
- if (pts[(index + 1) % n].fY == pts[index].fY) { |
- int maxIndex; |
- int minIndex = find_min_max_x_at_y(pts, index, n, &maxIndex); |
- if (minIndex == maxIndex) { |
- goto TRY_CROSSPROD; |
- } |
- SkASSERT(pts[minIndex].fY == pts[index].fY); |
- SkASSERT(pts[maxIndex].fY == pts[index].fY); |
- SkASSERT(pts[minIndex].fX <= pts[maxIndex].fX); |
- // we just subtract the indices, and let that auto-convert to |
- // SkScalar, since we just want - or + to signal the direction. |
- cross = minIndex - maxIndex; |
- } else { |
- TRY_CROSSPROD: |
- // Find a next and prev index to use for the cross-product test, |
- // but we try to find pts that form non-zero vectors from pts[index] |
- // |
- // Its possible that we can't find two non-degenerate vectors, so |
- // we have to guard our search (e.g. all the pts could be in the |
- // same place). |
- |
- // we pass n - 1 instead of -1 so we don't foul up % operator by |
- // passing it a negative LH argument. |
- int prev = find_diff_pt(pts, index, n, n - 1); |
- if (prev == index) { |
- // completely degenerate, skip to next contour |
- continue; |
- } |
- int next = find_diff_pt(pts, index, n, 1); |
- SkASSERT(next != index); |
- cross = cross_prod(pts[prev], pts[index], pts[next]); |
- // if we get a zero and the points are horizontal, then we look at the spread in |
- // x-direction. We really should continue to walk away from the degeneracy until |
- // there is a divergence. |
- if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) { |
- // construct the subtract so we get the correct Direction below |
- cross = pts[index].fX - pts[next].fX; |
- } |
+ int next = find_diff_pt(pts, index, n, 1); |
+ SkASSERT(next != index); |
+ cross = cross_prod(pts[prev], pts[index], pts[next]); |
+ // if we get a zero and the points are horizontal, then we look at the spread in |
+ // x-direction. We really should continue to walk away from the degeneracy until |
+ // there is a divergence. |
+ if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) { |
+ // construct the subtract so we get the correct Direction below |
+ cross = pts[index].fX - pts[next].fX; |
} |
+ } |
- if (cross) { |
- // record our best guess so far |
- ymax = pts[index].fY; |
- ymaxCross = cross; |
- } |
+ if (cross) { |
+ // record our best guess so far |
+ ymax = pts[index].fY; |
+ ymaxCross = cross; |
} |
} |
if (ymaxCross) { |
@@ -2803,259 +2693,3 @@ bool SkPath::cheapComputeDirection(Direction* dir) const { |
} |
} |
-/////////////////////////////////////////////////////////////////////////////// |
- |
-static SkScalar eval_cubic_coeff(SkScalar A, SkScalar B, SkScalar C, |
- SkScalar D, SkScalar t) { |
- return SkScalarMulAdd(SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C), t, D); |
-} |
- |
-static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, |
- SkScalar t) { |
- SkScalar A = c3 + 3*(c1 - c2) - c0; |
- SkScalar B = 3*(c2 - c1 - c1 + c0); |
- SkScalar C = 3*(c1 - c0); |
- SkScalar D = c0; |
- return eval_cubic_coeff(A, B, C, D, t); |
-} |
- |
-/* Given 4 cubic points (either Xs or Ys), and a target X or Y, compute the |
- t value such that cubic(t) = target |
- */ |
-static bool chopMonoCubicAt(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3, |
- SkScalar target, SkScalar* t) { |
- // SkASSERT(c0 <= c1 && c1 <= c2 && c2 <= c3); |
- SkASSERT(c0 < target && target < c3); |
- |
- SkScalar D = c0 - target; |
- SkScalar A = c3 + 3*(c1 - c2) - c0; |
- SkScalar B = 3*(c2 - c1 - c1 + c0); |
- SkScalar C = 3*(c1 - c0); |
- |
- const SkScalar TOLERANCE = SK_Scalar1 / 4096; |
- SkScalar minT = 0; |
- SkScalar maxT = SK_Scalar1; |
- SkScalar mid; |
- int i; |
- for (i = 0; i < 16; i++) { |
- mid = SkScalarAve(minT, maxT); |
- SkScalar delta = eval_cubic_coeff(A, B, C, D, mid); |
- if (delta < 0) { |
- minT = mid; |
- delta = -delta; |
- } else { |
- maxT = mid; |
- } |
- if (delta < TOLERANCE) { |
- break; |
- } |
- } |
- *t = mid; |
- return true; |
-} |
- |
-template <size_t N> static void find_minmax(const SkPoint pts[], |
- SkScalar* minPtr, SkScalar* maxPtr) { |
- SkScalar min, max; |
- min = max = pts[0].fX; |
- for (size_t i = 1; i < N; ++i) { |
- min = SkMinScalar(min, pts[i].fX); |
- max = SkMaxScalar(max, pts[i].fX); |
- } |
- *minPtr = min; |
- *maxPtr = max; |
-} |
- |
-static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { |
- SkPoint storage[4]; |
- |
- int dir = 1; |
- if (pts[0].fY > pts[3].fY) { |
- storage[0] = pts[3]; |
- storage[1] = pts[2]; |
- storage[2] = pts[1]; |
- storage[3] = pts[0]; |
- pts = storage; |
- dir = -1; |
- } |
- if (y < pts[0].fY || y >= pts[3].fY) { |
- return 0; |
- } |
- |
- // quickreject or quickaccept |
- SkScalar min, max; |
- find_minmax<4>(pts, &min, &max); |
- if (x < min) { |
- return 0; |
- } |
- if (x > max) { |
- return dir; |
- } |
- |
- // compute the actual x(t) value |
- SkScalar t, xt; |
- if (chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t)) { |
- xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t); |
- } else { |
- SkScalar mid = SkScalarAve(pts[0].fY, pts[3].fY); |
- xt = y < mid ? pts[0].fX : pts[3].fX; |
- } |
- return xt < x ? dir : 0; |
-} |
- |
-static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) { |
- SkPoint dst[10]; |
- int n = SkChopCubicAtYExtrema(pts, dst); |
- int w = 0; |
- for (int i = 0; i <= n; ++i) { |
- w += winding_mono_cubic(&dst[i * 3], x, y); |
- } |
- return w; |
-} |
- |
-static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) { |
- SkScalar y0 = pts[0].fY; |
- SkScalar y2 = pts[2].fY; |
- |
- int dir = 1; |
- if (y0 > y2) { |
- SkTSwap(y0, y2); |
- dir = -1; |
- } |
- if (y < y0 || y >= y2) { |
- return 0; |
- } |
- |
- // bounds check on X (not required. is it faster?) |
-#if 0 |
- if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) { |
- return 0; |
- } |
-#endif |
- |
- SkScalar roots[2]; |
- int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY, |
- 2 * (pts[1].fY - pts[0].fY), |
- pts[0].fY - y, |
- roots); |
- SkASSERT(n <= 1); |
- SkScalar xt; |
- if (0 == n) { |
- SkScalar mid = SkScalarAve(y0, y2); |
- // Need [0] and [2] if dir == 1 |
- // and [2] and [0] if dir == -1 |
- xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX; |
- } else { |
- SkScalar t = roots[0]; |
- SkScalar C = pts[0].fX; |
- SkScalar A = pts[2].fX - 2 * pts[1].fX + C; |
- SkScalar B = 2 * (pts[1].fX - C); |
- xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C); |
- } |
- return xt < x ? dir : 0; |
-} |
- |
-static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) { |
- // return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0; |
- if (y0 == y1) { |
- return true; |
- } |
- if (y0 < y1) { |
- return y1 <= y2; |
- } else { |
- return y1 >= y2; |
- } |
-} |
- |
-static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) { |
- SkPoint dst[5]; |
- int n = 0; |
- |
- if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) { |
- n = SkChopQuadAtYExtrema(pts, dst); |
- pts = dst; |
- } |
- int w = winding_mono_quad(pts, x, y); |
- if (n > 0) { |
- w += winding_mono_quad(&pts[2], x, y); |
- } |
- return w; |
-} |
- |
-static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) { |
- SkScalar x0 = pts[0].fX; |
- SkScalar y0 = pts[0].fY; |
- SkScalar x1 = pts[1].fX; |
- SkScalar y1 = pts[1].fY; |
- |
- SkScalar dy = y1 - y0; |
- |
- int dir = 1; |
- if (y0 > y1) { |
- SkTSwap(y0, y1); |
- dir = -1; |
- } |
- if (y < y0 || y >= y1) { |
- return 0; |
- } |
- |
- SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) - |
- SkScalarMul(dy, x - pts[0].fX); |
- |
- if (SkScalarSignAsInt(cross) == dir) { |
- dir = 0; |
- } |
- return dir; |
-} |
- |
-static bool contains_inclusive(const SkRect& r, SkScalar x, SkScalar y) { |
- return r.fLeft <= x && x <= r.fRight && r.fTop <= y && y <= r.fBottom; |
-} |
- |
-bool SkPath::contains(SkScalar x, SkScalar y) const { |
- bool isInverse = this->isInverseFillType(); |
- if (this->isEmpty()) { |
- return isInverse; |
- } |
- |
- if (!contains_inclusive(this->getBounds(), x, y)) { |
- return isInverse; |
- } |
- |
- SkPath::Iter iter(*this, true); |
- bool done = false; |
- int w = 0; |
- do { |
- SkPoint pts[4]; |
- switch (iter.next(pts, false)) { |
- case SkPath::kMove_Verb: |
- case SkPath::kClose_Verb: |
- break; |
- case SkPath::kLine_Verb: |
- w += winding_line(pts, x, y); |
- break; |
- case SkPath::kQuad_Verb: |
- w += winding_quad(pts, x, y); |
- break; |
- case SkPath::kConic_Verb: |
- SkASSERT(0); |
- break; |
- case SkPath::kCubic_Verb: |
- w += winding_cubic(pts, x, y); |
- break; |
- case SkPath::kDone_Verb: |
- done = true; |
- break; |
- } |
- } while (!done); |
- |
- switch (this->getFillType()) { |
- case SkPath::kEvenOdd_FillType: |
- case SkPath::kInverseEvenOdd_FillType: |
- w &= 1; |
- break; |
- default: |
- break; |
- } |
- return SkToBool(w); |
-} |