Index: src/core/SkPath.cpp |
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp |
index af8b1aa56e2ba0a56e86d3d7aa23d190b94fdfbd..2f8eab20b27c2f63da3b78efb0bcaf7f932bc3ae 100644 |
--- a/src/core/SkPath.cpp |
+++ b/src/core/SkPath.cpp |
@@ -359,10 +359,13 @@ The test fails if: |
There's more than four changes of direction. |
There's a discontinuity on the line (e.g., a move in the middle) |
The line reverses direction. |
- The rectangle doesn't complete a cycle. |
The path contains a quadratic or cubic. |
The path contains fewer than four points. |
- The final point isn't equal to the first point. |
+ *The rectangle doesn't complete a cycle. |
+ *The final point isn't equal to the first point. |
+ |
+ *These last two conditions we relax if we have a 3-edge path that would |
+ form a rectangle if it were closed (as we do when we fill a path) |
It's OK if the path has: |
Several colinear line segments composing a rectangle side. |
@@ -374,7 +377,18 @@ must travel in opposite directions. |
FIXME: Allow colinear quads and cubics to be treated like lines. |
FIXME: If the API passes fill-only, return true if the filled stroke |
is a rectangle, though the caller failed to close the path. |
+ |
+ first,last,next direction state-machine: |
+ 0x1 is set if the segment is horizontal |
+ 0x2 is set if the segment is moving to the right or down |
+ thus: |
+ two directions are opposites iff (dirA ^ dirB) == 0x2 |
+ two directions are perpendicular iff (dirA ^ dirB) == 0x1 |
+ |
*/ |
+static int rect_make_dir(SkScalar dx, SkScalar dy) { |
+ return ((0 != dx) << 0) | ((dx > 0 || dy > 0) << 1); |
+} |
bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr, |
bool* isClosed, Direction* direction) const { |
int corners = 0; |
@@ -407,8 +421,7 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts |
if (left == right && top == bottom) { |
break; // single point on side OK |
} |
- nextDirection = (left != right) << 0 | |
- (left < right || top < bottom) << 1; |
+ nextDirection = rect_make_dir(right - left, bottom - top); |
if (0 == corners) { |
firstDirection = nextDirection; |
first = last; |
@@ -460,6 +473,25 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts |
} |
// Success if 4 corners and first point equals last |
bool result = 4 == corners && (first == last || autoClose); |
+ if (!result) { |
+ // check if we are just an incomplete rectangle, in which case we can |
+ // return true, but not claim to be closed. |
+ // e.g. |
+ // 3 sided rectangle |
+ // 4 sided but the last edge is not long enough to reach the start |
+ // |
+ SkScalar closeX = first.x() - last.x(); |
+ SkScalar closeY = first.y() - last.y(); |
+ if (closeX && closeY) { |
+ return false; // we're diagonal, abort (can we ever reach this?) |
+ } |
+ int closeDirection = rect_make_dir(closeX, closeY); |
+ // make sure the close-segment doesn't double-back on itself |
+ if (3 == corners || (4 == corners && closeDirection == lastDirection)) { |
+ result = true; |
+ autoClose = false; // we are not closed |
+ } |
+ } |
if (savePts) { |
*ptsPtr = savePts; |
} |