blob: 0917b69e475ab19069e3736f6a60e2da5d9441df [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;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000016 bool firstPass = true;
17 SkPoint lastTopLeft;
caryclark@google.com07393ca2013-04-08 11:47:37 +000018 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
19 do {
20 int index, endIndex;
21 bool topDone;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000022 lastTopLeft = topLeft;
caryclark@google.com570863f2013-09-16 15:55:01 +000023 SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000024 &index, &endIndex, &topLeft, &topUnsortable, &topDone, firstPass);
caryclark@google.com07393ca2013-04-08 11:47:37 +000025 if (!current) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000026 if ((!topUnsortable || firstPass) && !topDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000027 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
28 topLeft.fX = topLeft.fY = SK_ScalarMin;
29 continue;
30 }
31 break;
32 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000033 firstPass = !topUnsortable || lastTopLeft != topLeft;
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 SkTDArray<SkOpSpan*> chaseArray;
35 do {
36 if (current->activeWinding(index, endIndex)) {
37 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000038 if (!unsortable && current->done()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000039 if (simple->isEmpty()) {
40 simple->init();
41 break;
42 }
43 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000044 SkASSERT(unsortable || !current->done());
45 int nextStart = index;
46 int nextEnd = endIndex;
47 SkOpSegment* next = current->findNextWinding(&chaseArray, &nextStart, &nextEnd,
48 &unsortable);
49 if (!next) {
50 if (!unsortable && simple->hasMove()
51 && current->verb() != SkPath::kLine_Verb
52 && !simple->isClosed()) {
53 current->addCurveTo(index, endIndex, simple, true);
54 SkASSERT(simple->isClosed());
55 }
56 break;
57 }
58 #if DEBUG_FLOW
59 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
60 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
61 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
62 #endif
63 current->addCurveTo(index, endIndex, simple, true);
64 current = next;
65 index = nextStart;
66 endIndex = nextEnd;
67 } while (!simple->isClosed() && (!unsortable
68 || !current->done(SkMin32(index, endIndex))));
69 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
caryclark@google.coma5e55922013-05-07 18:51:31 +000070 SkASSERT(unsortable || simple->isEmpty());
caryclark@google.com07393ca2013-04-08 11:47:37 +000071 int min = SkMin32(index, endIndex);
72 if (!current->done(min)) {
73 current->addCurveTo(index, endIndex, simple, true);
74 current->markDoneUnary(min);
75 }
76 }
77 simple->close();
78 } else {
79 SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000080 if (last && !last->fChased && !last->fLoop) {
81 last->fChased = true;
82 SkASSERT(!SkPathOpsDebug::ChaseContains(chaseArray, last));
83 // assert that last isn't already in array
caryclark@google.com07393ca2013-04-08 11:47:37 +000084 *chaseArray.append() = last;
85 }
86 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000087 SkTDArray<SkOpSpan *>* chaseArrayPtr = &chaseArray;
88 current = FindChase(chaseArrayPtr, &index, &endIndex);
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);
97 return simple->someAssemblyRequired();
98}
99
100// returns true if all edges were processed
caryclark@google.comd892bd82013-06-17 14:10:36 +0000101static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102 SkOpSegment* current;
103 int start, end;
104 bool unsortable = false;
105 bool closable = true;
106 while ((current = FindUndone(contourList, &start, &end))) {
107 do {
108 #if DEBUG_ACTIVE_SPANS
109 if (!unsortable && current->done()) {
110 DebugShowActiveSpans(contourList);
111 }
112 #endif
113 SkASSERT(unsortable || !current->done());
114 int nextStart = start;
115 int nextEnd = end;
116 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
117 if (!next) {
118 if (!unsortable && simple->hasMove()
119 && current->verb() != SkPath::kLine_Verb
120 && !simple->isClosed()) {
121 current->addCurveTo(start, end, simple, true);
122 SkASSERT(simple->isClosed());
123 }
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__,
128 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
129 current->xyAtT(end).fX, current->xyAtT(end).fY);
130 #endif
131 current->addCurveTo(start, end, simple, true);
132 current = next;
133 start = nextStart;
134 end = nextEnd;
135 } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
136 if (!simple->isClosed()) {
137 SkASSERT(unsortable);
138 int min = SkMin32(start, end);
139 if (!current->done(min)) {
140 current->addCurveTo(start, end, simple, true);
141 current->markDone(min, 1);
142 }
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) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000155#if DEBUG_SORT || DEBUG_SWAP_TOP
caryclark@google.com570863f2013-09-16 15:55:01 +0000156 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157#endif
158 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000159 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
160 : SkPath::kEvenOdd_FillType;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000161
162 // turn path into list of segments
163 SkTArray<SkOpContour> contours;
164 SkOpEdgeBuilder builder(path, contours);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000165 if (!builder.finish()) {
166 return false;
167 }
caryclark@google.comd892bd82013-06-17 14:10:36 +0000168 SkTArray<SkOpContour*, true> contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000169 MakeContourList(contours, contourList, false, false);
170 SkOpContour** currentPtr = contourList.begin();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000171 result->reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +0000172 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000173 if (!currentPtr) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000174 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000175 }
176 SkOpContour** listEnd = contourList.end();
177 // find all intersections between segments
178 do {
179 SkOpContour** nextPtr = currentPtr;
180 SkOpContour* current = *currentPtr++;
181 if (current->containsCubics()) {
182 AddSelfIntersectTs(current);
183 }
184 SkOpContour* next;
185 do {
186 next = *nextPtr++;
187 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
188 } while (currentPtr != listEnd);
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000189 if (!HandleCoincidence(&contourList, 0)) {
190 return false;
191 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000192 // construct closed contours
caryclark@google.com66560ca2013-04-26 19:51:16 +0000193 SkPathWriter simple(*result);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000194 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
195 : !bridgeXor(contourList, &simple))
196 { // if some edges could not be resolved, assemble remaining fragments
197 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000198 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000199 SkPathWriter assembled(temp);
200 Assemble(simple, &assembled);
201 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000202 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000204 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205}