a more involved path is rect bug
This is bug number ten in the series, and is the most interesting. It exploits that the code tracks corners 0, 2, and 3 but not corner 1. Changing the code to track all corners is the biggest so far, and while it (hopefully) simplifies things, the presence of new code may signify more bugs to come. R=robertphillips@google.com Bug: 824145,skia::7792 Change-Id: Ia18e4d80fbed06ae6d9c89dcb4c462c5610213cc Reviewed-on: https://skia-review.googlesource.com/121487 Reviewed-by: Robert Phillips <robertphillips@google.com> Commit-Queue: Cary Clark <caryclark@google.com>
This commit is contained in:
parent
93b4ddd347
commit
48c464a3c4
@ -433,7 +433,7 @@ DEF_SIMPLE_GM(rotatedcubicpath, canvas, 200, 200) {
|
||||
DEF_GM( return new PathFillGM; )
|
||||
DEF_GM( return new PathInverseFillGM; )
|
||||
|
||||
DEF_SIMPLE_GM(bug7792, canvas, 600, 600) {
|
||||
DEF_SIMPLE_GM(bug7792, canvas, 600, 800) {
|
||||
// from skbug.com/7792 bug description
|
||||
SkPaint p;
|
||||
SkPath path;
|
||||
@ -536,4 +536,15 @@ DEF_SIMPLE_GM(bug7792, canvas, 600, 600) {
|
||||
path.lineTo(75, 150);
|
||||
path.close();
|
||||
canvas->drawPath(path, p);
|
||||
// from skbug.com/7792#c29
|
||||
canvas->translate(-200 * 2, 200);
|
||||
path.reset();
|
||||
path.moveTo(75, 75);
|
||||
path.lineTo(150, 75);
|
||||
path.lineTo(150, 150);
|
||||
path.lineTo(75, 150);
|
||||
path.lineTo(75, 250);
|
||||
path.moveTo(75, 75);
|
||||
path.close();
|
||||
canvas->drawPath(path, p);
|
||||
}
|
||||
|
@ -435,7 +435,7 @@ 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:
|
||||
directions values:
|
||||
0x1 is set if the segment is horizontal
|
||||
0x2 is set if the segment is moving to the right or down
|
||||
thus:
|
||||
@ -456,9 +456,7 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
|
||||
const SkPoint* pts = *ptsPtr;
|
||||
const SkPoint* savePts = nullptr; // used to allow caller to iterate through a pair of rects
|
||||
lineStart.set(0, 0);
|
||||
int firstDirection = 0;
|
||||
int lastDirection = 0;
|
||||
int nextDirection = 0;
|
||||
signed char directions[] = {-1, -1, -1, -1}; // -1 to 3; -1 is uninitialized
|
||||
bool closedOrMoved = false;
|
||||
bool addedLine = false;
|
||||
bool autoClose = false;
|
||||
@ -486,9 +484,9 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
|
||||
break; // single point on side OK
|
||||
}
|
||||
addedLine = true;
|
||||
nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY);
|
||||
int nextDirection = rect_make_dir(lineDelta.fX, lineDelta.fY); // 0 to 3
|
||||
if (0 == corners) {
|
||||
firstDirection = nextDirection;
|
||||
directions[0] = nextDirection;
|
||||
lineStart = lineEnd;
|
||||
corners = 1;
|
||||
closedOrMoved = false;
|
||||
@ -497,26 +495,27 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
|
||||
if (closedOrMoved) {
|
||||
return false; // closed followed by a line
|
||||
}
|
||||
if (autoClose && nextDirection == firstDirection) {
|
||||
if (autoClose && nextDirection == directions[0]) {
|
||||
break; // colinear with first
|
||||
}
|
||||
closedOrMoved = autoClose;
|
||||
if (lastDirection != nextDirection) {
|
||||
if (++corners > 4) {
|
||||
return false; // too many direction changes
|
||||
}
|
||||
}
|
||||
lineStart = lineEnd;
|
||||
if (lastDirection == nextDirection) {
|
||||
if (directions[corners - 1] == nextDirection) {
|
||||
break; // colinear segment
|
||||
}
|
||||
// Possible values for corners are 2, 3, and 4.
|
||||
// When corners == 3, nextDirection opposes firstDirection.
|
||||
// Otherwise, nextDirection at corner 2 opposes corner 4.
|
||||
int turn = firstDirection ^ (corners - 1);
|
||||
int directionCycle = 3 == corners ? 0 : nextDirection ^ turn;
|
||||
if ((directionCycle ^ turn) != nextDirection) {
|
||||
return false; // direction didn't follow cycle
|
||||
if (corners >= 4) {
|
||||
return false; // too many direction changes
|
||||
}
|
||||
directions[corners++] = nextDirection;
|
||||
// opposite lines must point in opposite directions; xoring them should equal 2
|
||||
if (corners == 3) {
|
||||
if ((directions[0] ^ directions[2]) != 2) {
|
||||
return false;
|
||||
}
|
||||
} else if (corners == 4) {
|
||||
if ((directions[1] ^ directions[3]) != 2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -525,7 +524,7 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
|
||||
case kCubic_Verb:
|
||||
return false; // quadratic, cubic not allowed
|
||||
case kMove_Verb:
|
||||
if (allowPartial && !autoClose && firstDirection) {
|
||||
if (allowPartial && !autoClose && directions[0] >= 0) {
|
||||
insertClose = true;
|
||||
*currVerb -= 1; // try move again afterwards
|
||||
goto addMissingClose;
|
||||
@ -544,7 +543,6 @@ bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts
|
||||
break;
|
||||
}
|
||||
*currVerb += 1;
|
||||
lastDirection = nextDirection;
|
||||
addMissingClose:
|
||||
;
|
||||
}
|
||||
@ -567,7 +565,7 @@ addMissingClose:
|
||||
}
|
||||
int closeDirection = rect_make_dir(closeXY.fX, closeXY.fY);
|
||||
// make sure the close-segment doesn't double-back on itself
|
||||
if (3 == corners || closeDirection == lastDirection) {
|
||||
if (3 == corners || (closeDirection ^ directions[1]) == 2) {
|
||||
result = true;
|
||||
autoClose = false; // we are not closed
|
||||
}
|
||||
@ -583,7 +581,7 @@ addMissingClose:
|
||||
*isClosed = autoClose;
|
||||
}
|
||||
if (result && direction) {
|
||||
*direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
|
||||
*direction = directions[0] == ((directions[1] + 1) & 3) ? kCW_Direction : kCCW_Direction;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -4996,4 +4996,11 @@ DEF_TEST(Path_isRect, reporter) {
|
||||
REPORTER_ASSERT(reporter, path.isRect(&rect, nullptr, nullptr));
|
||||
compare.set(&points23[0], SK_ARRAY_COUNT(points23));
|
||||
REPORTER_ASSERT(reporter, rect == compare);
|
||||
// isolated from skbug.com/7792#c29
|
||||
SkPath::Verb verbs29[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb,
|
||||
SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb,
|
||||
SkPath::kClose_Verb };
|
||||
SkPoint points29[] = { {75, 75}, {150, 75}, {150, 150}, {75, 150}, {75, 250}, {75, 75} };
|
||||
path = makePath2(points29, verbs29, SK_ARRAY_COUNT(verbs29));
|
||||
REPORTER_ASSERT(reporter, !path.isRect(&rect, nullptr, nullptr));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user