blob: d587c3bbbb9244a1f1c3d00c26e5b31e134a747f [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 */
7#include "SkAddIntersections.h"
caryclark54359292015-03-26 07:52:43 -07008#include "SkOpCoincidence.h"
caryclark@google.com07393ca2013-04-08 11:47:37 +00009#include "SkOpEdgeBuilder.h"
10#include "SkPathOpsCommon.h"
11#include "SkPathWriter.h"
12
caryclarkeed356d2016-09-14 07:18:20 -070013static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000014 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 do {
caryclark624637c2015-05-11 07:21:27 -070016 SkOpSpan* span = FindSortableTop(contourList);
17 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -070018 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000019 }
caryclark624637c2015-05-11 07:21:27 -070020 SkOpSegment* current = span->segment();
21 SkOpSpanBase* start = span->next();
22 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -070023 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000024 do {
caryclark54359292015-03-26 07:52:43 -070025 if (current->activeWinding(start, end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 if (!unsortable && current->done()) {
caryclarkdac1d172014-06-17 05:15:38 -070028 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +000029 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000030 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -070031 SkOpSpanBase* nextStart = start;
32 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -070033 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 &unsortable);
35 if (!next) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000036 break;
37 }
38 #if DEBUG_FLOW
39 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070040 current->debugID(), start->pt().fX, start->pt().fY,
41 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000042 #endif
caryclarkef784fb2015-10-30 12:03:06 -070043 if (!current->addCurveTo(start, end, simple)) {
44 return false;
45 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 current = next;
caryclark54359292015-03-26 07:52:43 -070047 start = nextStart;
48 end = nextEnd;
49 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
50 if (current->activeWinding(start, end) && !simple->isClosed()) {
51 SkOpSpan* spanStart = start->starter(end);
52 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -070053 if (!current->addCurveTo(start, end, simple)) {
54 return false;
55 }
caryclark54359292015-03-26 07:52:43 -070056 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 }
58 }
caryclarkeed356d2016-09-14 07:18:20 -070059 simple->finishContour();
caryclark@google.com07393ca2013-04-08 11:47:37 +000060 } else {
caryclark54359292015-03-26 07:52:43 -070061 SkOpSpanBase* last = current->markAndChaseDone(start, end);
62 if (last && !last->chased()) {
63 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070064 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
caryclarkdac1d172014-06-17 05:15:38 -070065 *chase.append() = last;
66#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070067 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
68 if (!last->final()) {
69 SkDebugf(" windSum=%d", last->upCast()->windSum());
70 }
71 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -070072#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000073 }
74 }
caryclark54359292015-03-26 07:52:43 -070075 current = FindChase(&chase, &start, &end);
caryclark55888e42016-07-18 10:01:36 -070076 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +000077 if (!current) {
78 break;
79 }
80 } while (true);
81 } while (true);
caryclarkef784fb2015-10-30 12:03:06 -070082 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000083}
84
85// returns true if all edges were processed
caryclarkeed356d2016-09-14 07:18:20 -070086static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000087 SkOpSegment* current;
caryclark54359292015-03-26 07:52:43 -070088 SkOpSpanBase* start;
89 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000091 while ((current = FindUndone(contourList, &start, &end))) {
92 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 if (!unsortable && current->done()) {
caryclark55888e42016-07-18 10:01:36 -070094 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +000095 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000096 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -070097 SkOpSpanBase* nextStart = start;
98 SkOpSpanBase* nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
100 if (!next) {
101 if (!unsortable && simple->hasMove()
102 && current->verb() != SkPath::kLine_Verb
103 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -0700104 if (!current->addCurveTo(start, end, simple)) {
105 return false;
106 }
caryclark54359292015-03-26 07:52:43 -0700107 if (!simple->isClosed()) {
caryclark55888e42016-07-18 10:01:36 -0700108 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark54359292015-03-26 07:52:43 -0700109 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000110 }
111 break;
112 }
113 #if DEBUG_FLOW
114 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700115 current->debugID(), start->pt().fX, start->pt().fY,
116 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000117 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700118 if (!current->addCurveTo(start, end, simple)) {
119 return false;
120 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000121 current = next;
122 start = nextStart;
123 end = nextEnd;
caryclark54359292015-03-26 07:52:43 -0700124 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000125 if (!simple->isClosed()) {
126 SkASSERT(unsortable);
caryclark54359292015-03-26 07:52:43 -0700127 SkOpSpan* spanStart = start->starter(end);
128 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700129 if (!current->addCurveTo(start, end, simple)) {
130 return false;
131 }
caryclark54359292015-03-26 07:52:43 -0700132 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000134 }
caryclarkeed356d2016-09-14 07:18:20 -0700135 simple->finishContour();
caryclark55888e42016-07-18 10:01:36 -0700136 SkPathOpsDebug::ShowActiveSpans(contourList);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000137 }
caryclarkef784fb2015-10-30 12:03:06 -0700138 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139}
140
141// FIXME : add this as a member of SkPath
caryclark55888e42016-07-18 10:01:36 -0700142bool SimplifyDebug(const SkPath& path, SkPath* result
143 SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
reed0dc4dd62015-03-24 13:55:33 -0700144 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
145 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
146 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700147 if (path.isConvex()) {
148 if (result != &path) {
149 *result = path;
150 }
151 result->setFillType(fillType);
152 return true;
153 }
reed0dc4dd62015-03-24 13:55:33 -0700154 // turn path into list of segments
caryclark55888e42016-07-18 10:01:36 -0700155 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
caryclark54359292015-03-26 07:52:43 -0700156 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700157 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark55888e42016-07-18 10:01:36 -0700158 SkOpGlobalState globalState(contourList, &allocator
159 SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
160 SkOpCoincidence coincidence(&globalState);
caryclark13260682016-10-24 05:10:14 -0700161#ifdef SK_DEBUG
162 if (SkPathOpsDebug::gDumpOp) {
163 SkPathOpsDebug::DumpSimplify(path, testName);
164 }
165#endif
caryclark55888e42016-07-18 10:01:36 -0700166 SkScalar scaleFactor = ScaleFactor(path);
167 SkPath scaledPath;
168 const SkPath* workingPath;
169 if (scaleFactor > SK_Scalar1) {
170 ScalePath(path, 1.f / scaleFactor, &scaledPath);
171 workingPath = &scaledPath;
172 } else {
173 workingPath = &path;
174 }
caryclark624637c2015-05-11 07:21:27 -0700175#if DEBUG_SORT
caryclark54359292015-03-26 07:52:43 -0700176 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
177#endif
caryclarkeed356d2016-09-14 07:18:20 -0700178 SkOpEdgeBuilder builder(*workingPath, contourList, &globalState);
caryclark55888e42016-07-18 10:01:36 -0700179 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000180 return false;
181 }
caryclark03b03ca2015-04-23 09:13:37 -0700182#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700183 contour.dumpSegments();
caryclark54359292015-03-26 07:52:43 -0700184#endif
caryclark624637c2015-05-11 07:21:27 -0700185 if (!SortContourList(&contourList, false, false)) {
caryclark1049f122015-04-20 08:31:59 -0700186 result->reset();
187 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000188 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000190 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700191 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192 do {
caryclark624637c2015-05-11 07:21:27 -0700193 SkOpContour* next = current;
caryclark55888e42016-07-18 10:01:36 -0700194 while (AddIntersectTs(current, next, &coincidence)
caryclark624637c2015-05-11 07:21:27 -0700195 && (next = next->next()));
196 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700197#if DEBUG_VALIDATE
Cary Clarkab87d7a2016-10-04 10:01:04 -0400198 globalState.setPhase(SkOpPhase::kWalking);
caryclark54359292015-03-26 07:52:43 -0700199#endif
Cary Clarkab87d7a2016-10-04 10:01:04 -0400200 bool success = HandleCoincidence(contourList, &coincidence);
201#if DEBUG_COIN
202 globalState.debugAddToGlobalCoinDicts();
203#endif
204 if (!success) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000205 return false;
206 }
caryclark26ad22a2015-10-16 09:03:38 -0700207#if DEBUG_DUMP_ALIGNMENT
208 contour.dumpSegments("aligned");
209#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700211 result->reset();
212 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700213 SkPathWriter wrapper(*result);
caryclarkeed356d2016-09-14 07:18:20 -0700214 if (builder.xorMask() == kWinding_PathOpsMask ? !bridgeWinding(contourList, &wrapper)
215 : !bridgeXor(contourList, &wrapper)) {
caryclarkef784fb2015-10-30 12:03:06 -0700216 return false;
217 }
caryclarkeed356d2016-09-14 07:18:20 -0700218 wrapper.assemble(); // if some edges could not be resolved, assemble remaining
caryclark55888e42016-07-18 10:01:36 -0700219 if (scaleFactor > 1) {
220 ScalePath(*result, scaleFactor, result);
221 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000222 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000223}
caryclark5b5ddd72015-05-18 05:12:56 -0700224
caryclark55888e42016-07-18 10:01:36 -0700225bool Simplify(const SkPath& path, SkPath* result) {
caryclark13260682016-10-24 05:10:14 -0700226#ifdef SK_DEBUG
227 if (SkPathOpsDebug::gVerifyOp) {
228 if (!SimplifyDebug(path, result SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
229 SkPathOpsDebug::ReportSimplifyFail(path);
230 return false;
231 }
232 SkPathOpsDebug::VerifySimplify(path, *result);
233 return true;
234 }
235#endif
caryclark55888e42016-07-18 10:01:36 -0700236 return SimplifyDebug(path, result SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
237}