| Index: src/pathops/SkDCubicIntersection.cpp
|
| diff --git a/src/pathops/SkDCubicIntersection.cpp b/src/pathops/SkDCubicIntersection.cpp
|
| index 63d434f2fa972faf6acfc9261f17fca4ea1df1dd..ce2344841b30c5db598811775fcdc783c2831767 100644
|
| --- a/src/pathops/SkDCubicIntersection.cpp
|
| +++ b/src/pathops/SkDCubicIntersection.cpp
|
| @@ -15,8 +15,8 @@
|
| #include "SkTSort.h"
|
|
|
| #if ONE_OFF_DEBUG
|
| -static const double tLimits1[2][2] = {{0.388600450, 0.388600452}, {0.245852802, 0.245852804}};
|
| -static const double tLimits2[2][2] = {{-0.865211397, -0.865215212}, {-0.865207696, -0.865208078}};
|
| +static const double tLimits1[2][2] = {{0.3, 0.4}, {0.8, 0.9}};
|
| +static const double tLimits2[2][2] = {{-0.8, -0.9}, {-0.8, -0.9}};
|
| #endif
|
|
|
| #define DEBUG_QUAD_PART ONE_OFF_DEBUG && 1
|
| @@ -124,7 +124,8 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC
|
| SkDPoint p1 = cubic1.ptAtT(to1);
|
| SkDPoint p2 = cubic2.ptAtT(to2);
|
| if (p1.approximatelyEqual(p2)) {
|
| - SkASSERT(!locals.isCoincident(tIdx));
|
| + // FIXME: local edge may be coincident -- experiment with not propagating coincidence to caller
|
| +// SkASSERT(!locals.isCoincident(tIdx));
|
| if (&cubic1 != &cubic2 || !approximately_equal(to1, to2)) {
|
| if (i.swapped()) { // FIXME: insert should respect swap
|
| i.insert(to2, to1, p1);
|
| @@ -249,39 +250,70 @@ static void intersect(const SkDCubic& cubic1, double t1s, double t1e, const SkDC
|
| i.downDepth();
|
| }
|
|
|
| + // if two ends intersect, check middle for coincidence
|
| +bool SkIntersections::cubicCheckCoincidence(const SkDCubic& c1, const SkDCubic& c2) {
|
| + if (fUsed < 2) {
|
| + return false;
|
| + }
|
| + int last = fUsed - 1;
|
| + double tRange1 = fT[0][last] - fT[0][0];
|
| + double tRange2 = fT[1][last] - fT[1][0];
|
| + for (int index = 1; index < 5; ++index) {
|
| + double testT1 = fT[0][0] + tRange1 * index / 5;
|
| + double testT2 = fT[1][0] + tRange2 * index / 5;
|
| + SkDPoint testPt1 = c1.ptAtT(testT1);
|
| + SkDPoint testPt2 = c2.ptAtT(testT2);
|
| + if (!testPt1.approximatelyEqual(testPt2)) {
|
| + return false;
|
| + }
|
| + }
|
| + if (fUsed > 2) {
|
| + fPt[1] = fPt[last];
|
| + fT[0][1] = fT[0][last];
|
| + fT[1][1] = fT[1][last];
|
| + fUsed = 2;
|
| + }
|
| + fIsCoincident[0] = fIsCoincident[1] = 0x03;
|
| + return true;
|
| +}
|
| +
|
| #define LINE_FRACTION 0.1
|
|
|
| // intersect the end of the cubic with the other. Try lines from the end to control and opposite
|
| // end to determine range of t on opposite cubic.
|
| -static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
|
| - const SkDRect& bounds2, bool selfIntersect, SkIntersections& i) {
|
| - SkDLine line;
|
| +bool SkIntersections::cubicExactEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2) {
|
| int t1Index = start ? 0 : 3;
|
| - bool swap = i.swapped();
|
| double testT = (double) !start;
|
| + bool swap = swapped();
|
| // quad/quad at this point checks to see if exact matches have already been found
|
| // cubic/cubic can't reject so easily since cubics can intersect same point more than once
|
| - if (!selfIntersect) {
|
| - SkDLine tmpLine;
|
| - tmpLine[0] = tmpLine[1] = cubic2[t1Index];
|
| - tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
|
| - tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
|
| - SkIntersections impTs;
|
| - impTs.intersectRay(cubic1, tmpLine);
|
| - for (int index = 0; index < impTs.used(); ++index) {
|
| - SkDPoint realPt = impTs.pt(index);
|
| - if (!tmpLine[0].approximatelyEqualHalf(realPt)) {
|
| - continue;
|
| - }
|
| - if (swap) {
|
| - i.insert(testT, impTs[0][index], tmpLine[0]);
|
| - } else {
|
| - i.insert(impTs[0][index], testT, tmpLine[0]);
|
| - }
|
| - return;
|
| + SkDLine tmpLine;
|
| + tmpLine[0] = tmpLine[1] = cubic2[t1Index];
|
| + tmpLine[1].fX += cubic2[2 - start].fY - cubic2[t1Index].fY;
|
| + tmpLine[1].fY -= cubic2[2 - start].fX - cubic2[t1Index].fX;
|
| + SkIntersections impTs;
|
| + impTs.intersectRay(cubic1, tmpLine);
|
| + for (int index = 0; index < impTs.used(); ++index) {
|
| + SkDPoint realPt = impTs.pt(index);
|
| + if (!tmpLine[0].approximatelyEqual(realPt)) {
|
| + continue;
|
| + }
|
| + if (swap) {
|
| + insert(testT, impTs[0][index], tmpLine[0]);
|
| + } else {
|
| + insert(impTs[0][index], testT, tmpLine[0]);
|
| }
|
| + return true;
|
| }
|
| - // don't bother if the two cubics are connnected
|
| + return false;
|
| +}
|
| +
|
| +void SkIntersections::cubicNearEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cubic2,
|
| + const SkDRect& bounds2) {
|
| + SkDLine line;
|
| + int t1Index = start ? 0 : 3;
|
| + double testT = (double) !start;
|
| + // don't bother if the two cubics are connnected
|
| static const int kPointsInCubic = 4; // FIXME: move to DCubic, replace '4' with this
|
| static const int kMaxLineCubicIntersections = 3;
|
| SkSTArray<(kMaxLineCubicIntersections - 1) * kMaxLineCubicIntersections, double, true> tVals;
|
| @@ -310,10 +342,10 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub
|
| continue;
|
| }
|
| if (local.pt(idx2).approximatelyEqual(line[0])) {
|
| - if (i.swapped()) { // FIXME: insert should respect swap
|
| - i.insert(foundT, testT, line[0]);
|
| + if (swapped()) { // FIXME: insert should respect swap
|
| + insert(foundT, testT, line[0]);
|
| } else {
|
| - i.insert(testT, foundT, line[0]);
|
| + insert(testT, foundT, line[0]);
|
| }
|
| } else {
|
| tVals.push_back(foundT);
|
| @@ -334,12 +366,12 @@ static void intersectEnd(const SkDCubic& cubic1, bool start, const SkDCubic& cub
|
| }
|
| double tMin2 = SkTMax(tVals[tIdx] - LINE_FRACTION, 0.0);
|
| double tMax2 = SkTMin(tVals[tLast] + LINE_FRACTION, 1.0);
|
| - int lastUsed = i.used();
|
| - intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
|
| - if (lastUsed == i.used()) {
|
| + int lastUsed = used();
|
| + ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
|
| + if (lastUsed == used()) {
|
| tMin2 = SkTMax(tVals[tIdx] - (1.0 / SkDCubic::gPrecisionUnit), 0.0);
|
| tMax2 = SkTMin(tVals[tLast] + (1.0 / SkDCubic::gPrecisionUnit), 1.0);
|
| - intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, i);
|
| + ::intersect(cubic1, tMin1, tMax1, cubic2, tMin2, tMax2, 1, *this);
|
| }
|
| tIdx = tLast + 1;
|
| } while (tIdx < tVals.count());
|
| @@ -404,15 +436,19 @@ tryNextHalfPlane:
|
| }
|
|
|
| int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
|
| + if (fMax == 0) {
|
| + fMax = 9;
|
| + }
|
| bool selfIntersect = &c1 == &c2;
|
| if (selfIntersect) {
|
| - if (c1[0].approximatelyEqualHalf(c1[3])) {
|
| + if (c1[0].approximatelyEqual(c1[3])) {
|
| insert(0, 1, c1[0]);
|
| + return fUsed;
|
| }
|
| } else {
|
| for (int i1 = 0; i1 < 4; i1 += 3) {
|
| for (int i2 = 0; i2 < 4; i2 += 3) {
|
| - if (c1[i1].approximatelyEqualHalf(c2[i2])) {
|
| + if (c1[i1].approximatelyEqual(c2[i2])) {
|
| insert(i1 >> 1, i2 >> 1, c1[i1]);
|
| }
|
| }
|
| @@ -429,47 +465,47 @@ int SkIntersections::intersect(const SkDCubic& c1, const SkDCubic& c2) {
|
| }
|
| // quad/quad does linear test here -- cubic does not
|
| // cubics which are really lines should have been detected in reduce step earlier
|
| - SkDRect c1Bounds, c2Bounds;
|
| - // FIXME: pass in cached bounds from caller
|
| - c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
|
| - c2Bounds.setBounds(c2);
|
| - intersectEnd(c1, false, c2, c2Bounds, selfIntersect, *this);
|
| - intersectEnd(c1, true, c2, c2Bounds, selfIntersect, *this);
|
| + int exactEndBits = 0;
|
| if (selfIntersect) {
|
| if (fUsed) {
|
| return fUsed;
|
| }
|
| } else {
|
| + exactEndBits |= cubicExactEnd(c1, false, c2) << 0;
|
| + exactEndBits |= cubicExactEnd(c1, true, c2) << 1;
|
| swap();
|
| - intersectEnd(c2, false, c1, c1Bounds, false, *this);
|
| - intersectEnd(c2, true, c1, c1Bounds, false, *this);
|
| + exactEndBits |= cubicExactEnd(c2, false, c1) << 2;
|
| + exactEndBits |= cubicExactEnd(c2, true, c1) << 3;
|
| swap();
|
| }
|
| - // if two ends intersect, check middle for coincidence
|
| - if (fUsed >= 2) {
|
| + if (cubicCheckCoincidence(c1, c2)) {
|
| SkASSERT(!selfIntersect);
|
| - int last = fUsed - 1;
|
| - double tRange1 = fT[0][last] - fT[0][0];
|
| - double tRange2 = fT[1][last] - fT[1][0];
|
| - for (int index = 1; index < 5; ++index) {
|
| - double testT1 = fT[0][0] + tRange1 * index / 5;
|
| - double testT2 = fT[1][0] + tRange2 * index / 5;
|
| - SkDPoint testPt1 = c1.ptAtT(testT1);
|
| - SkDPoint testPt2 = c2.ptAtT(testT2);
|
| - if (!testPt1.approximatelyEqual(testPt2)) {
|
| - goto skipCoincidence;
|
| - }
|
| + return fUsed;
|
| + }
|
| + // FIXME: pass in cached bounds from caller
|
| + SkDRect c1Bounds, c2Bounds;
|
| + c1Bounds.setBounds(c1); // OPTIMIZE use setRawBounds ?
|
| + c2Bounds.setBounds(c2);
|
| + if (!(exactEndBits & 1)) {
|
| + cubicNearEnd(c1, false, c2, c2Bounds);
|
| + }
|
| + if (!(exactEndBits & 2)) {
|
| + cubicNearEnd(c1, true, c2, c2Bounds);
|
| + }
|
| + if (!selfIntersect) {
|
| + swap();
|
| + if (!(exactEndBits & 4)) {
|
| + cubicNearEnd(c2, false, c1, c1Bounds);
|
| }
|
| - if (fUsed > 2) {
|
| - fPt[1] = fPt[last];
|
| - fT[0][1] = fT[0][last];
|
| - fT[1][1] = fT[1][last];
|
| - fUsed = 2;
|
| + if (!(exactEndBits & 8)) {
|
| + cubicNearEnd(c2, true, c1, c1Bounds);
|
| }
|
| - fIsCoincident[0] = fIsCoincident[1] = 0x03;
|
| + swap();
|
| + }
|
| + if (cubicCheckCoincidence(c1, c2)) {
|
| + SkASSERT(!selfIntersect);
|
| return fUsed;
|
| }
|
| -skipCoincidence:
|
| ::intersect(c1, 0, 1, c2, 0, 1, 1, *this);
|
| // If an end point and a second point very close to the end is returned, the second
|
| // point may have been detected because the approximate quads
|
| @@ -501,9 +537,11 @@ skipCoincidence:
|
| }
|
| }
|
| if (match) {
|
| +#if DEBUG_CONCIDENT
|
| if (((match + 1) & match) != 0) {
|
| SkDebugf("%s coincident hole\n", __FUNCTION__);
|
| }
|
| +#endif
|
| // for now, assume that everything from start to finish is coincident
|
| if (fUsed > 2) {
|
| fPt[1] = fPt[last];
|
| @@ -522,6 +560,7 @@ skipCoincidence:
|
| // OPTIMIZATION If this is a common use case, optimize by duplicating
|
| // the intersect 3 loop to avoid the promotion / demotion code
|
| int SkIntersections::intersect(const SkDCubic& cubic, const SkDQuad& quad) {
|
| + fMax = 6;
|
| SkDCubic up = quad.toCubic();
|
| (void) intersect(cubic, up);
|
| return used();
|
| @@ -535,6 +574,7 @@ describes a method to find the self intersection of a cubic by taking the gradie
|
| form dotted with the normal, and solving for the roots. My math foo is too poor to implement this.*/
|
|
|
| int SkIntersections::intersect(const SkDCubic& c) {
|
| + fMax = 1;
|
| // check to see if x or y end points are the extrema. Are other quick rejects possible?
|
| if (c.endsAreExtremaInXOrY()) {
|
| return false;
|
|
|