blob: 11b6833a73fa39fbbc7f6f9252d1176d8e34e095 [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,
caryclark54359292015-03-26 07:52:43 -070014 SkChunkAlloc* allocator) {
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()) {
caryclark54359292015-03-26 07:52:43 -070040 current->addCurveTo(start, end, simple, true);
41 #if DEBUG_ACTIVE_SPANS
42 if (!simple->isClosed()) {
43 DebugShowActiveSpans(contourList);
44 }
45 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000046 }
47 break;
48 }
49 #if DEBUG_FLOW
50 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -070051 current->debugID(), start->pt().fX, start->pt().fY,
52 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000053 #endif
caryclark54359292015-03-26 07:52:43 -070054 current->addCurveTo(start, end, simple, true);
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 current = next;
caryclark54359292015-03-26 07:52:43 -070056 start = nextStart;
57 end = nextEnd;
58 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
59 if (current->activeWinding(start, end) && !simple->isClosed()) {
60 SkOpSpan* spanStart = start->starter(end);
61 if (!spanStart->done()) {
62 current->addCurveTo(start, end, simple, true);
63 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +000064 }
65 }
66 simple->close();
67 } else {
caryclark54359292015-03-26 07:52:43 -070068 SkOpSpanBase* last = current->markAndChaseDone(start, end);
69 if (last && !last->chased()) {
70 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -070071 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
caryclarkdac1d172014-06-17 05:15:38 -070072 *chase.append() = last;
73#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -070074 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
75 if (!last->final()) {
76 SkDebugf(" windSum=%d", last->upCast()->windSum());
77 }
78 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -070079#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000080 }
81 }
caryclark54359292015-03-26 07:52:43 -070082 current = FindChase(&chase, &start, &end);
caryclark@google.com07393ca2013-04-08 11:47:37 +000083 #if DEBUG_ACTIVE_SPANS
84 DebugShowActiveSpans(contourList);
85 #endif
86 if (!current) {
87 break;
88 }
89 } while (true);
90 } while (true);
91 return simple->someAssemblyRequired();
92}
93
94// returns true if all edges were processed
caryclark624637c2015-05-11 07:21:27 -070095static bool bridgeXor(SkOpContourHead* contourList, SkPathWriter* simple,
caryclark54359292015-03-26 07:52:43 -070096 SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000097 SkOpSegment* current;
caryclark54359292015-03-26 07:52:43 -070098 SkOpSpanBase* start;
99 SkOpSpanBase* end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000100 bool unsortable = false;
101 bool closable = true;
102 while ((current = FindUndone(contourList, &start, &end))) {
103 do {
104 #if DEBUG_ACTIVE_SPANS
105 if (!unsortable && current->done()) {
106 DebugShowActiveSpans(contourList);
107 }
108 #endif
109 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700110 SkOpSpanBase* nextStart = start;
111 SkOpSpanBase* nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000112 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
113 if (!next) {
114 if (!unsortable && simple->hasMove()
115 && current->verb() != SkPath::kLine_Verb
116 && !simple->isClosed()) {
117 current->addCurveTo(start, end, simple, true);
caryclark54359292015-03-26 07:52:43 -0700118 #if DEBUG_ACTIVE_SPANS
119 if (!simple->isClosed()) {
120 DebugShowActiveSpans(contourList);
121 }
122 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000123 }
124 break;
125 }
126 #if DEBUG_FLOW
127 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
caryclark54359292015-03-26 07:52:43 -0700128 current->debugID(), start->pt().fX, start->pt().fY,
129 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000130 #endif
131 current->addCurveTo(start, end, simple, true);
132 current = next;
133 start = nextStart;
134 end = nextEnd;
caryclark54359292015-03-26 07:52:43 -0700135 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000136 if (!simple->isClosed()) {
137 SkASSERT(unsortable);
caryclark54359292015-03-26 07:52:43 -0700138 SkOpSpan* spanStart = start->starter(end);
139 if (!spanStart->done()) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000140 current->addCurveTo(start, end, simple, true);
caryclark54359292015-03-26 07:52:43 -0700141 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000142 }
143 closable = false;
144 }
145 simple->close();
146 #if DEBUG_ACTIVE_SPANS
147 DebugShowActiveSpans(contourList);
148 #endif
149 }
150 return closable;
151}
152
153// FIXME : add this as a member of SkPath
caryclark@google.com66560ca2013-04-26 19:51:16 +0000154bool Simplify(const SkPath& path, SkPath* result) {
caryclark54359292015-03-26 07:52:43 -0700155 SkChunkAlloc allocator(4096); // FIXME: constant-ize, tune
reed0dc4dd62015-03-24 13:55:33 -0700156 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
157 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
158 : SkPath::kEvenOdd_FillType;
caryclark54359292015-03-26 07:52:43 -0700159 if (path.isConvex()) {
160 if (result != &path) {
161 *result = path;
162 }
163 result->setFillType(fillType);
164 return true;
165 }
reed0dc4dd62015-03-24 13:55:33 -0700166 // turn path into list of segments
caryclark54359292015-03-26 07:52:43 -0700167 SkOpCoincidence coincidence;
168 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700169 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
170 SkOpGlobalState globalState(&coincidence, contourList);
171#if DEBUG_SORT
caryclark54359292015-03-26 07:52:43 -0700172 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
173#endif
174 SkOpEdgeBuilder builder(path, &contour, &allocator, &globalState);
175 if (!builder.finish(&allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000176 return false;
177 }
caryclark03b03ca2015-04-23 09:13:37 -0700178#if DEBUG_DUMP_SEGMENTS
caryclark54359292015-03-26 07:52:43 -0700179 contour.dumpSegments((SkPathOp) -1);
180#endif
caryclark624637c2015-05-11 07:21:27 -0700181 if (!SortContourList(&contourList, false, false)) {
caryclark1049f122015-04-20 08:31:59 -0700182 result->reset();
183 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000184 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000185 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000186 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700187 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 do {
caryclark624637c2015-05-11 07:21:27 -0700189 SkOpContour* next = current;
190 while (AddIntersectTs(current, next, &coincidence, &allocator)
191 && (next = next->next()));
192 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700193#if DEBUG_VALIDATE
194 globalState.setPhase(SkOpGlobalState::kWalking);
195#endif
caryclark624637c2015-05-11 07:21:27 -0700196 if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000197 return false;
198 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000199 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700200 result->reset();
201 result->setFillType(fillType);
caryclark54359292015-03-26 07:52:43 -0700202 SkPathWriter wrapper(*result);
203 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &wrapper, &allocator)
204 : !bridgeXor(contourList, &wrapper, &allocator))
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205 { // if some edges could not be resolved, assemble remaining fragments
206 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000207 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000208 SkPathWriter assembled(temp);
caryclark54359292015-03-26 07:52:43 -0700209 Assemble(wrapper, &assembled);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000210 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000211 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000212 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000213 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000214}
caryclark5b5ddd72015-05-18 05:12:56 -0700215