blob: 97f0350db2574e1ecd058a43805c6a40e6b23e19 [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
caryclark624637c2015-05-11 07:21:27 -070013static bool bridgeWinding(SkOpContourHead* contourList, SkPathWriter* simple,
caryclarkef784fb2015-10-30 12:03:06 -070014 SkChunkAlloc* allocator, bool* closable) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000016 do {
caryclark624637c2015-05-11 07:21:27 -070017 SkOpSpan* span = FindSortableTop(contourList);
18 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -070019 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000020 }
caryclark624637c2015-05-11 07:21:27 -070021 SkOpSegment* current = span->segment();
22 SkOpSpanBase* start = span->next();
23 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -070024 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000025 do {
caryclark54359292015-03-26 07:52:43 -070026 if (current->activeWinding(start, end)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000028 if (!unsortable && current->done()) {
caryclarkdac1d172014-06-17 05:15:38 -070029 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +000030 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000031 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -070032 SkOpSpanBase* nextStart = start;
33 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -070034 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +000035 &unsortable);
36 if (!next) {
37 if (!unsortable && simple->hasMove()
38 && current->verb() != SkPath::kLine_Verb
39 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -070040 if (!current->addCurveTo(start, end, simple)) {
41 return false;
42 }
caryclark54359292015-03-26 07:52:43 -070043 #if DEBUG_ACTIVE_SPANS
44 if (!simple->isClosed()) {
45 DebugShowActiveSpans(contourList);
46 }
47 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000048 }
49 break;
50 }
51 #if DEBUG_FLOW
52 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070053 current->debugID(), start->pt().fX, start->pt().fY,
54 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 #endif
caryclarkef784fb2015-10-30 12:03:06 -070056 if (!current->addCurveTo(start, end, simple)) {
57 return false;
58 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000059 current = next;
caryclark54359292015-03-26 07:52:43 -070060 start = nextStart;
61 end = nextEnd;
62 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
63 if (current->activeWinding(start, end) && !simple->isClosed()) {
64 SkOpSpan* spanStart = start->starter(end);
65 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -070066 if (!current->addCurveTo(start, end, simple)) {
67 return false;
68 }
caryclark54359292015-03-26 07:52:43 -070069 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000070 }
71 }
72 simple->close();
73 } else {
caryclark54359292015-03-26 07:52:43 -070074 SkOpSpanBase* last = current->markAndChaseDone(start, end);
75 if (last && !last->chased()) {
76 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070077 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
caryclarkdac1d172014-06-17 05:15:38 -070078 *chase.append() = last;
79#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070080 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
81 if (!last->final()) {
82 SkDebugf(" windSum=%d", last->upCast()->windSum());
83 }
84 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -070085#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000086 }
87 }
caryclark54359292015-03-26 07:52:43 -070088 current = FindChase(&chase, &start, &end);
caryclark@google.com07393ca2013-04-08 11:47:37 +000089 #if DEBUG_ACTIVE_SPANS
90 DebugShowActiveSpans(contourList);
91 #endif
92 if (!current) {
93 break;
94 }
95 } while (true);
96 } while (true);
caryclarkef784fb2015-10-30 12:03:06 -070097 *closable = !simple->someAssemblyRequired();
98 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +000099}
100
101// returns true if all edges were processed
caryclark624637c2015-05-11 07:21:27 -0700102static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
caryclarkef784fb2015-10-30 12:03:06 -0700103 SkChunkAlloc* allocator, bool* closable) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000104 SkOpSegment* current;
caryclark54359292015-03-26 07:52:43 -0700105 SkOpSpanBase* start;
106 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000107 bool unsortable = false;
caryclarkef784fb2015-10-30 12:03:06 -0700108 *closable = true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000109 while ((current = FindUndone(contourList, &start, &end))) {
110 do {
111 #if DEBUG_ACTIVE_SPANS
112 if (!unsortable && current->done()) {
113 DebugShowActiveSpans(contourList);
114 }
115 #endif
116 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700117 SkOpSpanBase* nextStart = start;
118 SkOpSpanBase* nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000119 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
120 if (!next) {
121 if (!unsortable && simple->hasMove()
122 && current->verb() != SkPath::kLine_Verb
123 && !simple->isClosed()) {
caryclarkef784fb2015-10-30 12:03:06 -0700124 if (!current->addCurveTo(start, end, simple)) {
125 return false;
126 }
caryclark54359292015-03-26 07:52:43 -0700127 #if DEBUG_ACTIVE_SPANS
128 if (!simple->isClosed()) {
129 DebugShowActiveSpans(contourList);
130 }
131 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000132 }
133 break;
134 }
135 #if DEBUG_FLOW
136 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700137 current->debugID(), start->pt().fX, start->pt().fY,
138 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000139 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700140 if (!current->addCurveTo(start, end, simple)) {
141 return false;
142 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000143 current = next;
144 start = nextStart;
145 end = nextEnd;
caryclark54359292015-03-26 07:52:43 -0700146 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000147 if (!simple->isClosed()) {
148 SkASSERT(unsortable);
caryclark54359292015-03-26 07:52:43 -0700149 SkOpSpan* spanStart = start->starter(end);
150 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700151 if (!current->addCurveTo(start, end, simple)) {
152 return false;
153 }
caryclark54359292015-03-26 07:52:43 -0700154 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155 }
caryclarkef784fb2015-10-30 12:03:06 -0700156 *closable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 }
158 simple->close();
159 #if DEBUG_ACTIVE_SPANS
160 DebugShowActiveSpans(contourList);
161 #endif
162 }
caryclarkef784fb2015-10-30 12:03:06 -0700163 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000164}
165
166// FIXME : add this as a member of SkPath
caryclark@google.com66560ca2013-04-26 19:51:16 +0000167bool Simplify(const SkPath& path, SkPath* result) {
caryclark54359292015-03-26 07:52:43 -0700168 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
reed0dc4dd62015-03-24 13:55:33 -0700169 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
170 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
171 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700172 if (path.isConvex()) {
173 if (result != &path) {
174 *result = path;
175 }
176 result->setFillType(fillType);
177 return true;
178 }
reed0dc4dd62015-03-24 13:55:33 -0700179 // turn path into list of segments
caryclark54359292015-03-26 07:52:43 -0700180 SkOpCoincidence coincidence;
181 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700182 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
halcanary96fcdcc2015-08-27 07:41:13 -0700183 SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(nullptr));
caryclark624637c2015-05-11 07:21:27 -0700184#if DEBUG_SORT
caryclark54359292015-03-26 07:52:43 -0700185 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
186#endif
187 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
188 if (!builder.finish(&allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000189 return false;
190 }
caryclark03b03ca2015-04-23 09:13:37 -0700191#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700192 contour.dumpSegments();
caryclark54359292015-03-26 07:52:43 -0700193#endif
caryclark624637c2015-05-11 07:21:27 -0700194 if (!SortContourList(&contourList, false, false)) {
caryclark1049f122015-04-20 08:31:59 -0700195 result->reset();
196 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000197 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000198 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000199 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700200 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000201 do {
caryclark624637c2015-05-11 07:21:27 -0700202 SkOpContour* next = current;
203 while (AddIntersectTs(current, next, &coincidence, &allocator)
204 && (next = next->next()));
205 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700206#if DEBUG_VALIDATE
207 globalState.setPhase(SkOpGlobalState::kWalking);
208#endif
caryclark624637c2015-05-11 07:21:27 -0700209 if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000210 return false;
211 }
caryclark26ad22a2015-10-16 09:03:38 -0700212#if DEBUG_DUMP_ALIGNMENT
213 contour.dumpSegments("aligned");
214#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000215 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700216 result->reset();
217 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700218 SkPathWriter wrapper(*result);
robertphillips73add932016-04-07 09:01:20 -0700219 bool closable SK_INIT_TO_AVOID_WARNING;
caryclarkef784fb2015-10-30 12:03:06 -0700220 if (builder.xorMask() == kWinding_PathOpsMask
221 ? !bridgeWinding(contourList, &wrapper, &allocator, &closable)
222 : !bridgeXor(contourList, &wrapper, &allocator, &closable)) {
223 return false;
224 }
225 if (!closable)
caryclark@google.com07393ca2013-04-08 11:47:37 +0000226 { // if some edges could not be resolved, assemble remaining fragments
227 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000228 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000229 SkPathWriter assembled(temp);
caryclark54359292015-03-26 07:52:43 -0700230 Assemble(wrapper, &assembled);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000231 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000232 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000233 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000234 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000235}
caryclark5b5ddd72015-05-18 05:12:56 -0700236