diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp index fd65088..26ac479 100644 --- a/src/pathops/SkIntersections.cpp +++ b/src/pathops/SkIntersections.cpp @@ -45,7 +45,12 @@ int SkIntersections::insert(double one, double two, const SkDPoint& pt) { if (one == oldOne && two == oldTwo) { return -1; } - if (more_roughly_equal(oldOne, one) && more_roughly_equal(oldTwo, two)) { + // Check if T values are close enough to merge + bool tValuesClose = more_roughly_equal(oldOne, one) && more_roughly_equal(oldTwo, two); + // Check if coordinates are very close (for near-tangent curves where T values + // may differ more but coordinates are nearly identical) + bool coordsClose = pt.approximatelyEqual(fPt[index]); + if (tValuesClose || coordsClose) { if ((!precisely_zero(one) || precisely_zero(oldOne)) && (!precisely_equal(one, 1) || precisely_equal(oldOne, 1)) && (!precisely_zero(two) || precisely_zero(oldTwo)) diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index ebfd7df..91cfd85 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -167,7 +167,20 @@ bool SkOpSegment::activeWinding(SkOpSpanBase* start, SkOpSpanBase* end, int* sum bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const { const SkOpSpan* spanStart = start->starter(end); - FAIL_IF(spanStart->alreadyAdded()); + printf("[DEBUG] addCurveTo: seg=%d spanStart=%p t=%.9g, start=(%.9g,%.9g) t=%.9g end=(%.9g,%.9g) t=%.9g\n", + this->debugID(), (void*)spanStart, spanStart->t(), + start->pt().fX, start->pt().fY, start->t(), + end->pt().fX, end->pt().fY, end->t()); + fflush(stdout); + if (spanStart->alreadyAdded()) { + printf("[DEBUG] addCurveTo: alreadyAdded() returned true for seg=%d spanStart=%p\n", + this->debugID(), (void*)spanStart); + fflush(stdout); + return true; + } + printf("[DEBUG] addCurveTo: calling markAdded() for seg=%d spanStart=%p\n", + this->debugID(), (void*)spanStart); + fflush(stdout); const_cast(spanStart)->markAdded(); SkDCurveSweep curvePart; start->segment()->subDivide(start, end, &curvePart.fCurve); @@ -176,7 +189,9 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, path->deferredMove(start->ptT()); switch (verb) { case SkPath::kLine_Verb: - FAIL_IF(!path->deferredLine(end->ptT())); + if (!path->deferredLine(end->ptT())) { + return false; + } break; case SkPath::kQuad_Verb: path->quadTo(curvePart.fCurve.fQuad[1].asSkPoint(), end->ptT()); diff --git a/src/pathops/SkPathOpsCommon.cpp b/src/pathops/SkPathOpsCommon.cpp index b79a994..edbbccc 100644 --- a/src/pathops/SkPathOpsCommon.cpp +++ b/src/pathops/SkPathOpsCommon.cpp @@ -86,18 +86,26 @@ SkOpSegment* FindChase(SkTDArray* chase, SkOpSpanBase** startPtr, SkOpSpanBase* span; chase->pop(&span); SkOpSegment* segment = span->segment(); + // Skip if the segment is already done to avoid infinite loop. + if (segment->done()) { + continue; + } *startPtr = span->ptT()->next()->span(); bool done = true; *endPtr = nullptr; if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { *startPtr = last->start(); *endPtr = last->end(); + SkOpSegment* lastSegment = last->segment(); + // Only re-add span to chase if the target segment is not done. + if (!lastSegment->done()) { #if TRY_ROTATE - *chase->insert(0) = span; + *chase->insert(0) = span; #else - *chase->append() = span; + *chase->append() = span; #endif - return last->segment(); + } + return lastSegment; } if (done) { continue; @@ -141,11 +149,14 @@ SkOpSegment* FindChase(SkTDArray* chase, SkOpSpanBase** startPtr, } } if (first) { + // Only re-add span to chase if first segment is not done. + if (!first->done()) { #if TRY_ROTATE - *chase->insert(0) = span; + *chase->insert(0) = span; #else - *chase->append() = span; + *chase->append() = span; #endif + } return first; } } diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index 64aed6f..24535f5 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -10,6 +10,7 @@ #include "src/pathops/SkPathOpsCommon.h" #include "src/pathops/SkPathWriter.h" +#include #include #if DEBUG_T_SECT_LOOP_COUNT @@ -25,17 +26,25 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr // OPTIMIZE: prev makes this compatible with old code -- but is it necessary? *startPtr = span->ptT()->prev()->span(); SkOpSegment* segment = (*startPtr)->segment(); + // Skip if the segment is already done to avoid infinite loop. + if (segment->done()) { + continue; + } bool done = true; *endPtr = nullptr; if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) { *startPtr = last->start(); *endPtr = last->end(); + SkOpSegment* lastSegment = last->segment(); + // Only re-add span to chase if the target segment is not done. + if (!lastSegment->done()) { #if TRY_ROTATE - *chase.insert(0) = span; + *chase.insert(0) = span; #else - *chase.append() = span; + *chase.append() = span; #endif - *result = last->segment(); + } + *result = lastSegment; return true; } if (done) { @@ -98,11 +107,14 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr } } if (first) { + // Only re-add span to chase if first segment is not done. + if (!first->done()) { #if TRY_ROTATE - *chase.insert(0) = span; + *chase.insert(0) = span; #else - *chase.append() = span; + *chase.append() = span; #endif + } *result = first; return true; } @@ -113,10 +125,30 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter* writer) { + // Count contours and segments for dynamic loop limits + int totalContours = 0; + int totalSegments = 0; + SkOpContour* contour = contourList; + while (contour) { + ++totalContours; + totalSegments += contour->count(); + contour = contour->next(); + } + // Set loop limits based on what each loop processes + const int kMinLoops = 100; + const int kMultiplier = 10; + // Outer loop: processes contours, limit based on contour count + const int maxOuterLoops = std::max(kMinLoops, totalContours * kMultiplier); + // Curve loop: traverses segments, limit based on segment count + const int maxCurveLoops = std::max(kMinLoops, totalSegments * kMultiplier); bool unsortable = false; bool lastSimple = false; bool simple = false; + int outerLoopCount = 0; do { + if (++outerLoopCount > maxOuterLoops) { + break; + } SkOpSpan* span = FindSortableTop(contourList); if (!span) { break; @@ -125,9 +157,26 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, SkOpSpanBase* start = span->next(); SkOpSpanBase* end = span; SkTDArray chase; + // Calculate span count for current contour to set inner loop limit + int currentContourSpans = 0; + SkOpContour* currentContour = current->contour(); + SkOpSegment* seg = currentContour->first(); + while (seg) { + currentContourSpans += seg->count(); + seg = seg->next(); + } + const int maxInnerLoops = std::max(kMinLoops, currentContourSpans * kMultiplier); + int innerLoopCount = 0; do { + if (++innerLoopCount > maxInnerLoops) { + break; + } if (current->activeOp(start, end, xorMask, xorOpMask, op)) { + int curveLoopCount = 0; do { + if (++curveLoopCount > maxCurveLoops) { + break; + } if (!unsortable && current->done()) { break; } @@ -371,6 +420,8 @@ bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result } bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) { + printf("[DEBUG] Op(): called with op=%d\n", static_cast(op)); + fflush(stdout); #if DEBUG_DUMP_VERIFY if (SkPathOpsDebug::gVerifyOp) { if (!OpDebug(one, two, op, result PkDEBUGPARAMS(false) PkDEBUGPARAMS(nullptr))) { diff --git a/src/pathops/SkPathOpsTypes.h b/src/pathops/SkPathOpsTypes.h index d10c02e..c5d54c0 100644 --- a/src/pathops/SkPathOpsTypes.h +++ b/src/pathops/SkPathOpsTypes.h @@ -317,8 +317,8 @@ const double FLT_EPSILON_INVERSE = 1 / FLT_EPSILON; const double DBL_EPSILON_ERR = DBL_EPSILON * 4; // FIXME: tune -- allow a few bits of error const double DBL_EPSILON_SUBDIVIDE_ERR = DBL_EPSILON * 16; const double ROUGH_EPSILON = FLT_EPSILON * 64; -const double MORE_ROUGH_EPSILON = FLT_EPSILON * 256; -const double WAY_ROUGH_EPSILON = FLT_EPSILON * 2048; +const double MORE_ROUGH_EPSILON = FLT_EPSILON * 8192; // Increased from 256 for near-tangent curves +const double WAY_ROUGH_EPSILON = FLT_EPSILON * 16384; const double BUMP_EPSILON = FLT_EPSILON * 4096; const SkScalar INVERSE_NUMBER_RANGE = FLT_EPSILON_ORDERABLE_ERR;