blob: 2c89f35b22bb4e994bb1df12df7692d15ae2ec2b [file] [log] [blame]
caryclark@google.com07393ca2013-04-08 11:47:37 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
Mike Kleinc0bd9f92019-04-23 12:05:21 -05007#include "src/pathops/SkAddIntersections.h"
8#include "src/pathops/SkOpCoincidence.h"
9#include "src/pathops/SkOpEdgeBuilder.h"
10#include "src/pathops/SkPathOpsCommon.h"
11#include "src/pathops/SkPathWriter.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +000012
Ben Wagnerf08d1d02018-06-18 15:11:00 -040013#include <utility>
14
Cary Clark2587f412018-07-24 12:40:10 -040015static bool findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
16 SkOpSpanBase** endPtr, SkOpSegment** result) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000017 while (chase.count()) {
caryclark54359292015-03-26 07:52:43 -070018 SkOpSpanBase* span;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 chase.pop(&span);
caryclark54359292015-03-26 07:52:43 -070020 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
21 *startPtr = span->ptT()->prev()->span();
22 SkOpSegment* segment = (*startPtr)->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000023 bool done = true;
halcanary96fcdcc2015-08-27 07:41:13 -070024 *endPtr = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070025 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -070026 *startPtr = last->start();
27 *endPtr = last->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000028 #if TRY_ROTATE
29 *chase.insert(0) = span;
30 #else
31 *chase.append() = span;
32 #endif
Cary Clark2587f412018-07-24 12:40:10 -040033 *result = last->segment();
34 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000035 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000036 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000037 continue;
38 }
caryclarkbca19f72015-05-13 08:23:48 -070039 int winding;
40 bool sortable;
41 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclarkaa7ceb62016-06-29 10:46:08 -070042 if (!angle) {
Cary Clark2587f412018-07-24 12:40:10 -040043 *result = nullptr;
44 return true;
caryclarkaa7ceb62016-06-29 10:46:08 -070045 }
caryclark65f55312014-11-13 06:58:52 -080046 if (winding == SK_MinS32) {
47 continue;
48 }
caryclarkbca19f72015-05-13 08:23:48 -070049 int sumMiWinding, sumSuWinding;
50 if (sortable) {
51 segment = angle->segment();
52 sumMiWinding = segment->updateWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070053 if (sumMiWinding == SK_MinS32) {
54 SkASSERT(segment->globalState()->debugSkipAssert());
Cary Clark2587f412018-07-24 12:40:10 -040055 *result = nullptr;
56 return true;
caryclark79418092016-08-26 14:24:24 -070057 }
caryclarkbca19f72015-05-13 08:23:48 -070058 sumSuWinding = segment->updateOppWindingReverse(angle);
caryclark79418092016-08-26 14:24:24 -070059 if (sumSuWinding == SK_MinS32) {
60 SkASSERT(segment->globalState()->debugSkipAssert());
Cary Clark2587f412018-07-24 12:40:10 -040061 *result = nullptr;
62 return true;
caryclark79418092016-08-26 14:24:24 -070063 }
caryclarkbca19f72015-05-13 08:23:48 -070064 if (segment->operand()) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -040065 using std::swap;
66 swap(sumMiWinding, sumSuWinding);
caryclarkbca19f72015-05-13 08:23:48 -070067 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000068 }
halcanary96fcdcc2015-08-27 07:41:13 -070069 SkOpSegment* first = nullptr;
caryclarkbca19f72015-05-13 08:23:48 -070070 const SkOpAngle* firstAngle = angle;
caryclark54359292015-03-26 07:52:43 -070071 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000072 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -070073 SkOpSpanBase* start = angle->start();
74 SkOpSpanBase* end = angle->end();
Kevin Lubick42846132018-01-05 10:11:11 -050075 int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0;
caryclarkbca19f72015-05-13 08:23:48 -070076 if (sortable) {
77 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
78 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
79 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000080 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -070081 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000082 first = segment;
caryclark54359292015-03-26 07:52:43 -070083 *startPtr = start;
84 *endPtr = end;
caryclark65f55312014-11-13 06:58:52 -080085 }
caryclarkdac1d172014-06-17 05:15:38 -070086 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -070087 if (sortable) {
Cary Clark2587f412018-07-24 12:40:10 -040088 if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
89 oppSumWinding, angle, nullptr)) {
90 return false;
91 }
caryclarkbca19f72015-05-13 08:23:48 -070092 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000094 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000095 if (first) {
96 #if TRY_ROTATE
97 *chase.insert(0) = span;
98 #else
99 *chase.append() = span;
100 #endif
Cary Clark2587f412018-07-24 12:40:10 -0400101 *result = first;
102 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000103 }
104 }
Cary Clark2587f412018-07-24 12:40:10 -0400105 *result = nullptr;
106 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000107}
108
caryclark624637c2015-05-11 07:21:27 -0700109static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
Cary Clark1857ddb2018-07-11 11:01:43 -0400110 const int xorMask, const int xorOpMask, SkPathWriter* writer) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000111 bool unsortable = false;
Cary Clark1857ddb2018-07-11 11:01:43 -0400112 bool lastSimple = false;
113 bool simple = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000114 do {
caryclark624637c2015-05-11 07:21:27 -0700115 SkOpSpan* span = FindSortableTop(contourList);
116 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -0700117 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000118 }
caryclark624637c2015-05-11 07:21:27 -0700119 SkOpSegment* current = span->segment();
120 SkOpSpanBase* start = span->next();
121 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -0700122 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 do {
caryclark54359292015-03-26 07:52:43 -0700124 if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000125 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000126 if (!unsortable && current->done()) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000127 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000128 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000129 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700130 SkOpSpanBase* nextStart = start;
131 SkOpSpanBase* nextEnd = end;
Cary Clark1857ddb2018-07-11 11:01:43 -0400132 lastSimple = simple;
caryclarkdac1d172014-06-17 05:15:38 -0700133 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
Cary Clark1857ddb2018-07-11 11:01:43 -0400134 &unsortable, &simple, op, xorMask, xorOpMask);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000135 if (!next) {
Cary Clark1857ddb2018-07-11 11:01:43 -0400136 if (!unsortable && writer->hasMove()
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 && current->verb() != SkPath::kLine_Verb
Cary Clark1857ddb2018-07-11 11:01:43 -0400138 && !writer->isClosed()) {
139 if (!current->addCurveTo(start, end, writer)) {
caryclarkef784fb2015-10-30 12:03:06 -0700140 return false;
141 }
Cary Clark1857ddb2018-07-11 11:01:43 -0400142 if (!writer->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -0700143 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclarkdac1d172014-06-17 05:15:38 -0700144 }
Cary Clark1857ddb2018-07-11 11:01:43 -0400145 } else if (lastSimple) {
146 if (!current->addCurveTo(start, end, writer)) {
147 return false;
148 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000149 }
150 break;
151 }
152 #if DEBUG_FLOW
caryclark54359292015-03-26 07:52:43 -0700153 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
154 current->debugID(), start->pt().fX, start->pt().fY,
155 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000156 #endif
Cary Clark1857ddb2018-07-11 11:01:43 -0400157 if (!current->addCurveTo(start, end, writer)) {
caryclarkef784fb2015-10-30 12:03:06 -0700158 return false;
159 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000160 current = next;
caryclark54359292015-03-26 07:52:43 -0700161 start = nextStart;
162 end = nextEnd;
Cary Clark1857ddb2018-07-11 11:01:43 -0400163 } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
164 if (current->activeWinding(start, end) && !writer->isClosed()) {
caryclark54359292015-03-26 07:52:43 -0700165 SkOpSpan* spanStart = start->starter(end);
166 if (!spanStart->done()) {
Cary Clark1857ddb2018-07-11 11:01:43 -0400167 if (!current->addCurveTo(start, end, writer)) {
caryclarkef784fb2015-10-30 12:03:06 -0700168 return false;
169 }
caryclark54359292015-03-26 07:52:43 -0700170 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000171 }
172 }
Cary Clark1857ddb2018-07-11 11:01:43 -0400173 writer->finishContour();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000174 } else {
Cary Clarkc050a1a2018-06-25 08:45:40 -0400175 SkOpSpanBase* last;
176 if (!current->markAndChaseDone(start, end, &last)) {
177 return false;
178 }
caryclark54359292015-03-26 07:52:43 -0700179 if (last && !last->chased()) {
180 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -0700181 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
182 *chase.append() = last;
183#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700184 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
185 if (!last->final()) {
186 SkDebugf(" windSum=%d", last->upCast()->windSum());
187 }
188 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700189#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000190 }
191 }
Cary Clark2587f412018-07-24 12:40:10 -0400192 if (!findChaseOp(chase, &start, &end, &current)) {
193 return false;
194 }
caryclark55888e42016-07-18 10:01:36 -0700195 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000196 if (!current) {
197 break;
198 }
199 } while (true);
200 } while (true);
caryclarkeed356d2016-09-14 07:18:20 -0700201 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000202}
203
Cary Clarkd8a39a02018-01-09 16:56:46 -0500204// diagram of why this simplifcation is possible is here:
205// https://skia.org/dev/present/pathops link at bottom of the page
206// https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing
caryclark54359292015-03-26 07:52:43 -0700207static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000208// inside minuend outside minuend
209// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
caryclark54359292015-03-26 07:52:43 -0700210{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
211{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
212{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
213{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
214{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000215};
216
caryclark54359292015-03-26 07:52:43 -0700217static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000218 {{ false, false }, { true, false }}, // diff
219 {{ false, false }, { false, true }}, // sect
220 {{ false, true }, { true, true }}, // union
221 {{ false, true }, { true, false }}, // xor
222 {{ false, true }, { false, false }}, // rev diff
223};
224
caryclark26ad22a2015-10-16 09:03:38 -0700225#if DEBUG_T_SECT_LOOP_COUNT
226
Mike Kleinc0bd9f92019-04-23 12:05:21 -0500227#include "include/private/SkMutex.h"
caryclark26ad22a2015-10-16 09:03:38 -0700228
Cary Clark74b42902018-03-09 07:38:47 -0500229SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
caryclark26ad22a2015-10-16 09:03:38 -0700230
231void ReportPathOpsDebugging() {
232 debugWorstState.debugLoopReport();
233}
234
235extern void (*gVerboseFinalize)();
236
237#endif
238
caryclark3f0753d2016-06-28 09:23:57 -0700239bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
240 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500241#if DEBUG_DUMP_VERIFY
242#ifndef SK_DEBUG
243 const char* testName = "release";
244#endif
caryclark13260682016-10-24 05:10:14 -0700245 if (SkPathOpsDebug::gDumpOp) {
246 SkPathOpsDebug::DumpOp(one, two, op, testName);
247 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000248#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000249 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
Cary Clark1bb47df2018-06-18 08:53:00 -0400250 bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
Mike Reed7d34dc72019-11-26 12:17:17 -0500251 SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd :
252 SkPathFillType::kEvenOdd;
Cary Clark1bb47df2018-06-18 08:53:00 -0400253 SkRect rect1, rect2;
254 if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
255 result->reset();
256 result->setFillType(fillType);
257 if (rect1.intersect(rect2)) {
258 result->addRect(rect1);
259 }
260 return true;
261 }
262 if (one.isEmpty() || two.isEmpty()) {
263 SkPath work;
264 switch (op) {
265 case kIntersect_SkPathOp:
266 break;
267 case kUnion_SkPathOp:
268 case kXOR_SkPathOp:
269 work = one.isEmpty() ? two : one;
270 break;
271 case kDifference_SkPathOp:
272 if (!one.isEmpty()) {
273 work = one;
274 }
275 break;
276 case kReverseDifference_SkPathOp:
277 if (!two.isEmpty()) {
278 work = two;
279 }
280 break;
281 default:
282 SkASSERT(0); // unhandled case
283 }
284 if (inverseFill != work.isInverseFillType()) {
285 work.toggleInverseFillType();
286 }
287 return Simplify(work, result);
288 }
289 SkSTArenaAlloc<4096> allocator; // FIXME: add a constant expression here, tune
290 SkOpContour contour;
291 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
292 SkOpGlobalState globalState(contourList, &allocator
293 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
294 SkOpCoincidence coincidence(&globalState);
Cary Clark5de52332018-08-30 12:58:23 -0400295 const SkPath* minuend = &one;
296 const SkPath* subtrahend = &two;
caryclark54359292015-03-26 07:52:43 -0700297 if (op == kReverseDifference_SkPathOp) {
Ben Wagnerf08d1d02018-06-18 15:11:00 -0400298 using std::swap;
299 swap(minuend, subtrahend);
caryclark54359292015-03-26 07:52:43 -0700300 op = kDifference_SkPathOp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000301 }
caryclark624637c2015-05-11 07:21:27 -0700302#if DEBUG_SORT
caryclark@google.com570863f2013-09-16 15:55:01 +0000303 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000305 // turn path into list of segments
caryclarkeed356d2016-09-14 07:18:20 -0700306 SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
caryclarkd751ac02014-10-03 05:36:27 -0700307 if (builder.unparseable()) {
308 return false;
309 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000310 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000311 builder.addOperand(*subtrahend);
caryclark55888e42016-07-18 10:01:36 -0700312 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000313 return false;
314 }
caryclark03b03ca2015-04-23 09:13:37 -0700315#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700316 contourList->dumpSegments("seg", op);
caryclark54359292015-03-26 07:52:43 -0700317#endif
318
caryclark@google.com07393ca2013-04-08 11:47:37 +0000319 const int xorOpMask = builder.xorMask();
caryclark624637c2015-05-11 07:21:27 -0700320 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
321 xorOpMask == kEvenOdd_PathOpsMask)) {
caryclark1049f122015-04-20 08:31:59 -0700322 result->reset();
323 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000324 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000325 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000326 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700327 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000328 do {
caryclark624637c2015-05-11 07:21:27 -0700329 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700330 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700331 && (next = next->next()))
332 ;
333 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700334#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -0400335 globalState.setPhase(SkOpPhase::kWalking);
caryclark54359292015-03-26 07:52:43 -0700336#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400337 bool success = HandleCoincidence(contourList, &coincidence);
338#if DEBUG_COIN
339 globalState.debugAddToGlobalCoinDicts();
340#endif
341 if (!success) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000342 return false;
343 }
caryclark26ad22a2015-10-16 09:03:38 -0700344#if DEBUG_ALIGNMENT
345 contourList->dumpSegments("aligned");
346#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000347 // construct closed contours
Cary Clark2587f412018-07-24 12:40:10 -0400348 SkPath original = *result;
caryclark1049f122015-04-20 08:31:59 -0700349 result->reset();
350 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000351 SkPathWriter wrapper(*result);
caryclarkeed356d2016-09-14 07:18:20 -0700352 if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
Cary Clark2587f412018-07-24 12:40:10 -0400353 *result = original;
caryclarkeed356d2016-09-14 07:18:20 -0700354 return false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000355 }
caryclarkeed356d2016-09-14 07:18:20 -0700356 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
caryclark26ad22a2015-10-16 09:03:38 -0700357#if DEBUG_T_SECT_LOOP_COUNT
Herb Derby9c71e7b2019-06-17 14:40:42 -0400358 static SkMutex& debugWorstLoop = *(new SkMutex);
caryclark26ad22a2015-10-16 09:03:38 -0700359 {
Herb Derby9c71e7b2019-06-17 14:40:42 -0400360 SkAutoMutexExclusive autoM(debugWorstLoop);
caryclark26ad22a2015-10-16 09:03:38 -0700361 if (!gVerboseFinalize) {
362 gVerboseFinalize = &ReportPathOpsDebugging;
363 }
364 debugWorstState.debugDoYourWorst(&globalState);
365 }
366#endif
caryclark@google.com66560ca2013-04-26 19:51:16 +0000367 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000368}
caryclark624637c2015-05-11 07:21:27 -0700369
370bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
Cary Clark918fb1f2016-11-15 13:22:25 -0500371#if DEBUG_DUMP_VERIFY
caryclark13260682016-10-24 05:10:14 -0700372 if (SkPathOpsDebug::gVerifyOp) {
373 if (!OpDebug(one, two, op, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
374 SkPathOpsDebug::ReportOpFail(one, two, op);
375 return false;
376 }
377 SkPathOpsDebug::VerifyOp(one, two, op, *result);
378 return true;
caryclark26ad22a2015-10-16 09:03:38 -0700379 }
caryclark26ad22a2015-10-16 09:03:38 -0700380#endif
caryclark13260682016-10-24 05:10:14 -0700381 return OpDebug(one, two, op, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
caryclark624637c2015-05-11 07:21:27 -0700382}