blob: 66a6c40268eb71779389303a20d261dbd314c769 [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"
8#include "SkOpEdgeBuilder.h"
9#include "SkPathOpsCommon.h"
10#include "SkPathWriter.h"
11
caryclark@google.comd892bd82013-06-17 14:10:36 +000012static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000013 bool firstContour = true;
14 bool unsortable = false;
15 bool topUnsortable = false;
16 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
17 do {
18 int index, endIndex;
19 bool topDone;
caryclark@google.com570863f2013-09-16 15:55:01 +000020 SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
21 &index, &endIndex, &topLeft, &topUnsortable, &topDone);
caryclark@google.com07393ca2013-04-08 11:47:37 +000022 if (!current) {
23 if (topUnsortable || !topDone) {
24 topUnsortable = false;
25 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
26 topLeft.fX = topLeft.fY = SK_ScalarMin;
27 continue;
28 }
29 break;
30 }
31 SkTDArray<SkOpSpan*> chaseArray;
32 do {
33 if (current->activeWinding(index, endIndex)) {
34 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000035 if (!unsortable && current->done()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000036 if (simple->isEmpty()) {
37 simple->init();
38 break;
39 }
40 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000041 SkASSERT(unsortable || !current->done());
42 int nextStart = index;
43 int nextEnd = endIndex;
44 SkOpSegment* next = current->findNextWinding(&chaseArray, &nextStart, &nextEnd,
45 &unsortable);
46 if (!next) {
47 if (!unsortable && simple->hasMove()
48 && current->verb() != SkPath::kLine_Verb
49 && !simple->isClosed()) {
50 current->addCurveTo(index, endIndex, simple, true);
51 SkASSERT(simple->isClosed());
52 }
53 break;
54 }
55 #if DEBUG_FLOW
56 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
57 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
58 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
59 #endif
60 current->addCurveTo(index, endIndex, simple, true);
61 current = next;
62 index = nextStart;
63 endIndex = nextEnd;
64 } while (!simple->isClosed() && (!unsortable
65 || !current->done(SkMin32(index, endIndex))));
66 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000067 SkASSERT(unsortable || simple->isEmpty());
caryclark@google.com07393ca2013-04-08 11:47:37 +000068 int min = SkMin32(index, endIndex);
69 if (!current->done(min)) {
70 current->addCurveTo(index, endIndex, simple, true);
71 current->markDoneUnary(min);
72 }
73 }
74 simple->close();
75 } else {
76 SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000077 if (last && !last->fChased && !last->fLoop) {
78 last->fChased = true;
79 SkASSERT(!SkPathOpsDebug::ChaseContains(chaseArray, last));
80 // assert that last isn't already in array
caryclark@google.com07393ca2013-04-08 11:47:37 +000081 *chaseArray.append() = last;
82 }
83 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000084 SkTDArray<SkOpSpan *>* chaseArrayPtr = &chaseArray;
85 current = FindChase(chaseArrayPtr, &index, &endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +000086 #if DEBUG_ACTIVE_SPANS
87 DebugShowActiveSpans(contourList);
88 #endif
89 if (!current) {
90 break;
91 }
92 } while (true);
93 } while (true);
94 return simple->someAssemblyRequired();
95}
96
97// returns true if all edges were processed
caryclark@google.comd892bd82013-06-17 14:10:36 +000098static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 SkOpSegment* current;
100 int start, end;
101 bool unsortable = false;
102 bool closable = true;
103 while ((current = FindUndone(contourList, &start, &end))) {
104 do {
105 #if DEBUG_ACTIVE_SPANS
106 if (!unsortable && current->done()) {
107 DebugShowActiveSpans(contourList);
108 }
109 #endif
110 SkASSERT(unsortable || !current->done());
111 int nextStart = start;
112 int nextEnd = end;
113 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
114 if (!next) {
115 if (!unsortable && simple->hasMove()
116 && current->verb() != SkPath::kLine_Verb
117 && !simple->isClosed()) {
118 current->addCurveTo(start, end, simple, true);
119 SkASSERT(simple->isClosed());
120 }
121 break;
122 }
123 #if DEBUG_FLOW
124 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
125 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
126 current->xyAtT(end).fX, current->xyAtT(end).fY);
127 #endif
128 current->addCurveTo(start, end, simple, true);
129 current = next;
130 start = nextStart;
131 end = nextEnd;
132 } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
133 if (!simple->isClosed()) {
134 SkASSERT(unsortable);
135 int min = SkMin32(start, end);
136 if (!current->done(min)) {
137 current->addCurveTo(start, end, simple, true);
138 current->markDone(min, 1);
139 }
140 closable = false;
141 }
142 simple->close();
143 #if DEBUG_ACTIVE_SPANS
144 DebugShowActiveSpans(contourList);
145 #endif
146 }
147 return closable;
148}
149
150// FIXME : add this as a member of SkPath
caryclark@google.com66560ca2013-04-26 19:51:16 +0000151bool Simplify(const SkPath& path, SkPath* result) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000152#if DEBUG_SORT || DEBUG_SWAP_TOP
caryclark@google.com570863f2013-09-16 15:55:01 +0000153 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154#endif
155 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000156 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
157 : SkPath::kEvenOdd_FillType;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000158
159 // turn path into list of segments
160 SkTArray<SkOpContour> contours;
161 SkOpEdgeBuilder builder(path, contours);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000162 if (!builder.finish()) {
163 return false;
164 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000165 SkTArray<SkOpContour*, true> contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000166 MakeContourList(contours, contourList, false, false);
167 SkOpContour** currentPtr = contourList.begin();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000168 result->reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +0000169 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000170 if (!currentPtr) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000171 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000172 }
173 SkOpContour** listEnd = contourList.end();
174 // find all intersections between segments
175 do {
176 SkOpContour** nextPtr = currentPtr;
177 SkOpContour* current = *currentPtr++;
178 if (current->containsCubics()) {
179 AddSelfIntersectTs(current);
180 }
181 SkOpContour* next;
182 do {
183 next = *nextPtr++;
184 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
185 } while (currentPtr != listEnd);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000186 if (!HandleCoincidence(&contourList, 0)) {
187 return false;
188 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000189 // construct closed contours
caryclark@google.com66560ca2013-04-26 19:51:16 +0000190 SkPathWriter simple(*result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000191 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
192 : !bridgeXor(contourList, &simple))
193 { // if some edges could not be resolved, assemble remaining fragments
194 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000195 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000196 SkPathWriter assembled(temp);
197 Assemble(simple, &assembled);
198 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000199 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000201 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000202}