From 0f6d3b1c1dfe0486491aacfbcfbdc308c86b9876 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Fri, 13 Mar 2026 16:39:42 +0800 Subject: [PATCH 1/6] Fix PathOp failure when curves are nearly tangent. --- src/pathops/SkOpSegment.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index ebfd7df..f3d73c5 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -167,7 +167,11 @@ 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()); + if (spanStart->alreadyAdded()) { + // Skip already added edges instead of failing - this can happen + // with near-tangent curves producing tiny loops + return true; + } const_cast(spanStart)->markAdded(); SkDCurveSweep curvePart; start->segment()->subDivide(start, end, &curvePart.fCurve); @@ -176,7 +180,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()); From 4052cb75d9c0dc8a8fd47912df368f46e6edd2f0 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Mar 2026 16:07:20 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pathops/SkPathOpsCommon.cpp | 21 ++++++++++++++++----- src/pathops/SkPathOpsOp.cpp | 21 ++++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) 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..b9b0aa3 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -25,17 +25,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 +106,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; } From 33fc0e94b165b2d891318aa2cf2bcf5ade34e642 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Mar 2026 18:57:07 +0800 Subject: [PATCH 3/6] Fix PathOp failure when curves are nearly tangent. --- src/pathops/SkIntersections.cpp | 7 ++++++- src/pathops/SkOpSegment.cpp | 4 +--- 2 files changed, 7 insertions(+), 4 deletions(-) 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 f3d73c5..dc8553b 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -168,9 +168,7 @@ bool SkOpSegment::addCurveTo(const SkOpSpanBase* start, const SkOpSpanBase* end, SkPathWriter* path) const { const SkOpSpan* spanStart = start->starter(end); if (spanStart->alreadyAdded()) { - // Skip already added edges instead of failing - this can happen - // with near-tangent curves producing tiny loops - return true; + return false; } const_cast(spanStart)->markAdded(); SkDCurveSweep curvePart; From dd365f2402d07058e2ae66f35e003424cbefb320 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Mar 2026 20:16:31 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pathops/SkIntersections.cpp | 19 +++++++++++++++++ src/pathops/SkOpSegment.cpp | 35 ++++++++++++++++++++++++++++--- src/pathops/SkPathOpsOp.cpp | 37 +++++++++++++++++++++++++++++++++ src/pathops/SkPathOpsTypes.h | 4 ++-- 4 files changed, 90 insertions(+), 5 deletions(-) diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp index 26ac479..65d7d2a 100644 --- a/src/pathops/SkIntersections.cpp +++ b/src/pathops/SkIntersections.cpp @@ -33,6 +33,25 @@ void SkIntersections::flip() { } int SkIntersections::insert(double one, double two, const SkDPoint& pt) { + // Debug output for points near the problem area + bool nearProblem = (pt.fX > 5.0 && pt.fX < 5.5 && pt.fY > 15.5 && pt.fY < 16.0); + if (nearProblem) { + printf("[DEBUG-INSERT] pt=(%.9g,%.9g) t1=%.9g t2=%.9g fUsed=%d\n", + pt.fX, pt.fY, one, two, fUsed); + printf("[DEBUG-INSERT] MORE_ROUGH_EPSILON=%.9g\n", MORE_ROUGH_EPSILON); + for (int i = 0; i < fUsed; ++i) { + double t1Diff = fabs(fT[0][i] - one); + double t2Diff = fabs(fT[1][i] - two); + bool t1Close = more_roughly_equal(fT[0][i], one); + bool t2Close = more_roughly_equal(fT[1][i], two); + bool coordsClose = pt.approximatelyEqual(fPt[i]); + printf("[DEBUG-INSERT] existing[%d]: pt=(%.9g,%.9g) t1=%.9g t2=%.9g\n", + i, fPt[i].fX, fPt[i].fY, fT[0][i], fT[1][i]); + printf("[DEBUG-INSERT] t1Diff=%.9g t2Diff=%.9g t1Close=%d t2Close=%d coordsClose=%d\n", + t1Diff, t2Diff, t1Close, t2Close, coordsClose); + } + fflush(stdout); + } if (fIsCoincident[0] == 3 && between(fT[0][0], one, fT[0][1])) { // For now, don't allow a mix of coincident and non-coincident intersections return -1; diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index dc8553b..e58b690 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -167,9 +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); + 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()) { - return false; + 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); @@ -1055,15 +1066,33 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) { bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT, const SkPoint& testPt) const { PkASSERT(this == base->segment()); + // Debug: check if this is near the problem area + bool nearProblem = (testPt.fX > 5.0 && testPt.fX < 5.5 && testPt.fY > 15.5 && testPt.fY < 16.0); if (this == testParent) { if (precisely_equal(base->fT, testT)) { + if (nearProblem) { + printf("[DEBUG-MATCH] precisely_equal TRUE: baseT=%.9g testT=%.9g\n", base->fT, testT); + fflush(stdout); + } return true; } } - if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) { + bool approxEqual = SkDPoint::ApproximatelyEqual(testPt, base->fPt); + if (!approxEqual) { + if (nearProblem) { + printf("[DEBUG-MATCH] ApproximatelyEqual FALSE: basePt=(%.9g,%.9g) testPt=(%.9g,%.9g)\n", + base->fPt.fX, base->fPt.fY, testPt.fX, testPt.fY); + fflush(stdout); + } return false; } - return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt); + bool disjoint = (this == testParent) && this->ptsDisjoint(base->fT, base->fPt, testT, testPt); + if (nearProblem) { + printf("[DEBUG-MATCH] basePt=(%.9g,%.9g) baseT=%.9g testPt=(%.9g,%.9g) testT=%.9g approxEqual=%d disjoint=%d\n", + base->fPt.fX, base->fPt.fY, base->fT, testPt.fX, testPt.fY, testT, approxEqual, disjoint); + fflush(stdout); + } + return this != testParent || !disjoint; } static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) { diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index b9b0aa3..bf3db47 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -124,10 +124,19 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter* writer) { + printf("[DEBUG] bridgeOp: entered\n"); + fflush(stdout); bool unsortable = false; bool lastSimple = false; bool simple = false; + int outerLoopCount = 0; + const int kMaxOuterLoops = 10000; // Prevent infinite loops do { + if (++outerLoopCount > kMaxOuterLoops) { + printf("[DEBUG] bridgeOp: exceeded max outer loop count, breaking\n"); + fflush(stdout); + break; + } SkOpSpan* span = FindSortableTop(contourList); if (!span) { break; @@ -136,9 +145,23 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, SkOpSpanBase* start = span->next(); SkOpSpanBase* end = span; SkTDArray chase; + int innerLoopCount = 0; + const int kMaxInnerLoops = 10000; do { + if (++innerLoopCount > kMaxInnerLoops) { + printf("[DEBUG] bridgeOp: exceeded max inner loop count, breaking\n"); + fflush(stdout); + break; + } if (current->activeOp(start, end, xorMask, xorOpMask, op)) { + int curveLoopCount = 0; + const int kMaxCurveLoops = 10000; do { + if (++curveLoopCount > kMaxCurveLoops) { + printf("[DEBUG] bridgeOp: exceeded max curve loop count, breaking\n"); + fflush(stdout); + break; + } if (!unsortable && current->done()) { break; } @@ -153,6 +176,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, && current->verb() != SkPath::kLine_Verb && !writer->isClosed()) { if (!current->addCurveTo(start, end, writer)) { + printf("[DEBUG] bridgeOp: addCurveTo failed at line 157\n"); + fflush(stdout); return false; } if (!writer->isClosed()) { @@ -160,6 +185,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } } else if (lastSimple) { if (!current->addCurveTo(start, end, writer)) { + printf("[DEBUG] bridgeOp: addCurveTo failed at line 164\n"); + fflush(stdout); return false; } } @@ -171,6 +198,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, end->pt().fX, end->pt().fY); #endif if (!current->addCurveTo(start, end, writer)) { + printf("[DEBUG] bridgeOp: addCurveTo failed at line 175\n"); + fflush(stdout); return false; } current = next; @@ -181,6 +210,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, SkOpSpan* spanStart = start->starter(end); if (!spanStart->done()) { if (!current->addCurveTo(start, end, writer)) { + printf("[DEBUG] bridgeOp: addCurveTo failed at line 192\n"); + fflush(stdout); return false; } current->markDone(spanStart); @@ -190,6 +221,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } else { SkOpSpanBase* last; if (!current->markAndChaseDone(start, end, &last)) { + printf("[DEBUG] bridgeOp: markAndChaseDone failed\n"); + fflush(stdout); return false; } if (last && !last->chased()) { @@ -206,6 +239,8 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } } if (!findChaseOp(chase, &start, &end, ¤t)) { + printf("[DEBUG] bridgeOp: findChaseOp failed\n"); + fflush(stdout); return false; } SkPathOpsDebug::ShowActiveSpans(contourList); @@ -382,6 +417,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; From 531b09ac5e6287f296e7d4ff013069f8df455b52 Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Mar 2026 20:38:40 +0800 Subject: [PATCH 5/6] format code --- src/pathops/SkIntersections.cpp | 19 ------------ src/pathops/SkOpSegment.cpp | 22 ++------------ src/pathops/SkPathOpsOp.cpp | 51 ++++++++++++++++----------------- 3 files changed, 27 insertions(+), 65 deletions(-) diff --git a/src/pathops/SkIntersections.cpp b/src/pathops/SkIntersections.cpp index 65d7d2a..26ac479 100644 --- a/src/pathops/SkIntersections.cpp +++ b/src/pathops/SkIntersections.cpp @@ -33,25 +33,6 @@ void SkIntersections::flip() { } int SkIntersections::insert(double one, double two, const SkDPoint& pt) { - // Debug output for points near the problem area - bool nearProblem = (pt.fX > 5.0 && pt.fX < 5.5 && pt.fY > 15.5 && pt.fY < 16.0); - if (nearProblem) { - printf("[DEBUG-INSERT] pt=(%.9g,%.9g) t1=%.9g t2=%.9g fUsed=%d\n", - pt.fX, pt.fY, one, two, fUsed); - printf("[DEBUG-INSERT] MORE_ROUGH_EPSILON=%.9g\n", MORE_ROUGH_EPSILON); - for (int i = 0; i < fUsed; ++i) { - double t1Diff = fabs(fT[0][i] - one); - double t2Diff = fabs(fT[1][i] - two); - bool t1Close = more_roughly_equal(fT[0][i], one); - bool t2Close = more_roughly_equal(fT[1][i], two); - bool coordsClose = pt.approximatelyEqual(fPt[i]); - printf("[DEBUG-INSERT] existing[%d]: pt=(%.9g,%.9g) t1=%.9g t2=%.9g\n", - i, fPt[i].fX, fPt[i].fY, fT[0][i], fT[1][i]); - printf("[DEBUG-INSERT] t1Diff=%.9g t2Diff=%.9g t1Close=%d t2Close=%d coordsClose=%d\n", - t1Diff, t2Diff, t1Close, t2Close, coordsClose); - } - fflush(stdout); - } if (fIsCoincident[0] == 3 && between(fT[0][0], one, fT[0][1])) { // For now, don't allow a mix of coincident and non-coincident intersections return -1; diff --git a/src/pathops/SkOpSegment.cpp b/src/pathops/SkOpSegment.cpp index e58b690..91cfd85 100644 --- a/src/pathops/SkOpSegment.cpp +++ b/src/pathops/SkOpSegment.cpp @@ -1066,33 +1066,15 @@ bool SkOpSegment::markWinding(SkOpSpan* span, int winding, int oppWinding) { bool SkOpSegment::match(const SkOpPtT* base, const SkOpSegment* testParent, double testT, const SkPoint& testPt) const { PkASSERT(this == base->segment()); - // Debug: check if this is near the problem area - bool nearProblem = (testPt.fX > 5.0 && testPt.fX < 5.5 && testPt.fY > 15.5 && testPt.fY < 16.0); if (this == testParent) { if (precisely_equal(base->fT, testT)) { - if (nearProblem) { - printf("[DEBUG-MATCH] precisely_equal TRUE: baseT=%.9g testT=%.9g\n", base->fT, testT); - fflush(stdout); - } return true; } } - bool approxEqual = SkDPoint::ApproximatelyEqual(testPt, base->fPt); - if (!approxEqual) { - if (nearProblem) { - printf("[DEBUG-MATCH] ApproximatelyEqual FALSE: basePt=(%.9g,%.9g) testPt=(%.9g,%.9g)\n", - base->fPt.fX, base->fPt.fY, testPt.fX, testPt.fY); - fflush(stdout); - } + if (!SkDPoint::ApproximatelyEqual(testPt, base->fPt)) { return false; } - bool disjoint = (this == testParent) && this->ptsDisjoint(base->fT, base->fPt, testT, testPt); - if (nearProblem) { - printf("[DEBUG-MATCH] basePt=(%.9g,%.9g) baseT=%.9g testPt=(%.9g,%.9g) testT=%.9g approxEqual=%d disjoint=%d\n", - base->fPt.fX, base->fPt.fY, base->fT, testPt.fX, testPt.fY, testT, approxEqual, disjoint); - fflush(stdout); - } - return this != testParent || !disjoint; + return this != testParent || !this->ptsDisjoint(base->fT, base->fPt, testT, testPt); } static SkOpSegment* set_last(SkOpSpanBase** last, SkOpSpanBase* endSpan) { diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index bf3db47..9ca96a5 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 @@ -124,17 +125,33 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter* writer) { - printf("[DEBUG] bridgeOp: entered\n"); - fflush(stdout); + // Count total segments and spans for dynamic loop limits + int totalSegments = 0; + int totalSpans = 0; + SkOpContour* contour = contourList; + while (contour) { + int segCount = contour->count(); + totalSegments += segCount; + if (segCount > 0) { + SkOpSegment* seg = contour->first(); + while (seg) { + totalSpans += seg->count(); + seg = seg->next(); + } + } + contour = contour->next(); + } + // Set loop limits based on actual data size, with a reasonable minimum + // Each span could be visited multiple times in complex cases, so use a multiplier + const int kMinLoops = 100; + const int kMultiplier = 10; + const int maxLoops = std::max(kMinLoops, (totalSegments + totalSpans) * kMultiplier); bool unsortable = false; bool lastSimple = false; bool simple = false; int outerLoopCount = 0; - const int kMaxOuterLoops = 10000; // Prevent infinite loops do { - if (++outerLoopCount > kMaxOuterLoops) { - printf("[DEBUG] bridgeOp: exceeded max outer loop count, breaking\n"); - fflush(stdout); + if (++outerLoopCount > maxLoops) { break; } SkOpSpan* span = FindSortableTop(contourList); @@ -146,20 +163,14 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, SkOpSpanBase* end = span; SkTDArray chase; int innerLoopCount = 0; - const int kMaxInnerLoops = 10000; do { - if (++innerLoopCount > kMaxInnerLoops) { - printf("[DEBUG] bridgeOp: exceeded max inner loop count, breaking\n"); - fflush(stdout); + if (++innerLoopCount > maxLoops) { break; } if (current->activeOp(start, end, xorMask, xorOpMask, op)) { int curveLoopCount = 0; - const int kMaxCurveLoops = 10000; do { - if (++curveLoopCount > kMaxCurveLoops) { - printf("[DEBUG] bridgeOp: exceeded max curve loop count, breaking\n"); - fflush(stdout); + if (++curveLoopCount > maxLoops) { break; } if (!unsortable && current->done()) { @@ -176,8 +187,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, && current->verb() != SkPath::kLine_Verb && !writer->isClosed()) { if (!current->addCurveTo(start, end, writer)) { - printf("[DEBUG] bridgeOp: addCurveTo failed at line 157\n"); - fflush(stdout); return false; } if (!writer->isClosed()) { @@ -185,8 +194,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } } else if (lastSimple) { if (!current->addCurveTo(start, end, writer)) { - printf("[DEBUG] bridgeOp: addCurveTo failed at line 164\n"); - fflush(stdout); return false; } } @@ -198,8 +205,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, end->pt().fX, end->pt().fY); #endif if (!current->addCurveTo(start, end, writer)) { - printf("[DEBUG] bridgeOp: addCurveTo failed at line 175\n"); - fflush(stdout); return false; } current = next; @@ -210,8 +215,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, SkOpSpan* spanStart = start->starter(end); if (!spanStart->done()) { if (!current->addCurveTo(start, end, writer)) { - printf("[DEBUG] bridgeOp: addCurveTo failed at line 192\n"); - fflush(stdout); return false; } current->markDone(spanStart); @@ -221,8 +224,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } else { SkOpSpanBase* last; if (!current->markAndChaseDone(start, end, &last)) { - printf("[DEBUG] bridgeOp: markAndChaseDone failed\n"); - fflush(stdout); return false; } if (last && !last->chased()) { @@ -239,8 +240,6 @@ static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, } } if (!findChaseOp(chase, &start, &end, ¤t)) { - printf("[DEBUG] bridgeOp: findChaseOp failed\n"); - fflush(stdout); return false; } SkPathOpsDebug::ShowActiveSpans(contourList); From bc0ea21627562ff9d72b45c81e5fb9cc7bec2bca Mon Sep 17 00:00:00 2001 From: YGauroa Date: Tue, 17 Mar 2026 21:13:42 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pathops/SkPathOpsOp.cpp | 38 ++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/src/pathops/SkPathOpsOp.cpp b/src/pathops/SkPathOpsOp.cpp index 9ca96a5..24535f5 100644 --- a/src/pathops/SkPathOpsOp.cpp +++ b/src/pathops/SkPathOpsOp.cpp @@ -125,33 +125,28 @@ static bool findChaseOp(SkTDArray& chase, SkOpSpanBase** startPtr static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op, const int xorMask, const int xorOpMask, SkPathWriter* writer) { - // Count total segments and spans for dynamic loop limits + // Count contours and segments for dynamic loop limits + int totalContours = 0; int totalSegments = 0; - int totalSpans = 0; SkOpContour* contour = contourList; while (contour) { - int segCount = contour->count(); - totalSegments += segCount; - if (segCount > 0) { - SkOpSegment* seg = contour->first(); - while (seg) { - totalSpans += seg->count(); - seg = seg->next(); - } - } + ++totalContours; + totalSegments += contour->count(); contour = contour->next(); } - // Set loop limits based on actual data size, with a reasonable minimum - // Each span could be visited multiple times in complex cases, so use a multiplier + // Set loop limits based on what each loop processes const int kMinLoops = 100; const int kMultiplier = 10; - const int maxLoops = std::max(kMinLoops, (totalSegments + totalSpans) * kMultiplier); + // 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 > maxLoops) { + if (++outerLoopCount > maxOuterLoops) { break; } SkOpSpan* span = FindSortableTop(contourList); @@ -162,15 +157,24 @@ 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 > maxLoops) { + if (++innerLoopCount > maxInnerLoops) { break; } if (current->activeOp(start, end, xorMask, xorOpMask, op)) { int curveLoopCount = 0; do { - if (++curveLoopCount > maxLoops) { + if (++curveLoopCount > maxCurveLoops) { break; } if (!unsortable && current->done()) {