2 * Copyright 2012 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
7 #include "SkAddIntersections.h"
8 #include "SkOpEdgeBuilder.h"
9 #include "SkPathOpsCommon.h"
10 #include "SkPathWriter.h"
12 static bool bridgeWinding(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
13 bool firstContour = true;
14 bool unsortable = false;
15 bool topUnsortable = false;
16 bool firstPass = true;
18 SkPoint topLeft = {SK_ScalarMin, SK_ScalarMin};
22 bool onlyVertical = false;
23 lastTopLeft = topLeft;
24 SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
25 &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
27 if ((!topUnsortable || firstPass) && !topDone) {
28 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
29 topLeft.fX = topLeft.fY = SK_ScalarMin;
33 } else if (onlyVertical) {
36 firstPass = !topUnsortable || lastTopLeft != topLeft;
37 SkTDArray<SkOpSpan*> chase;
39 if (current->activeWinding(index, endIndex)) {
41 if (!unsortable && current->done()) {
44 SkASSERT(unsortable || !current->done());
45 int nextStart = index;
46 int nextEnd = endIndex;
47 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
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());
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);
63 current->addCurveTo(index, endIndex, simple, true);
67 } while (!simple->isClosed() && (!unsortable
68 || !current->done(SkMin32(index, endIndex))));
69 if (current->activeWinding(index, endIndex) && !simple->isClosed()) {
70 // SkASSERT(unsortable || simple->isEmpty());
71 int min = SkMin32(index, endIndex);
72 if (!current->done(min)) {
73 current->addCurveTo(index, endIndex, simple, true);
74 current->markDoneUnary(min);
79 SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
80 if (last && !last->fChased && !last->fLoop) {
82 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
83 // assert that last isn't already in array
84 *chase.append() = last;
86 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
87 last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
92 current = FindChase(&chase, &index, &endIndex);
93 #if DEBUG_ACTIVE_SPANS
94 DebugShowActiveSpans(contourList);
101 return simple->someAssemblyRequired();
104 // returns true if all edges were processed
105 static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
106 SkOpSegment* current;
108 bool unsortable = false;
109 bool closable = true;
110 while ((current = FindUndone(contourList, &start, &end))) {
112 #if DEBUG_ACTIVE_SPANS
113 if (!unsortable && current->done()) {
114 DebugShowActiveSpans(contourList);
117 SkASSERT(unsortable || !current->done());
118 int nextStart = start;
120 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
122 if (!unsortable && simple->hasMove()
123 && current->verb() != SkPath::kLine_Verb
124 && !simple->isClosed()) {
125 current->addCurveTo(start, end, simple, true);
126 SkASSERT(simple->isClosed());
131 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
132 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
133 current->xyAtT(end).fX, current->xyAtT(end).fY);
135 current->addCurveTo(start, end, simple, true);
139 } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
140 if (!simple->isClosed()) {
141 SkASSERT(unsortable);
142 int min = SkMin32(start, end);
143 if (!current->done(min)) {
144 current->addCurveTo(start, end, simple, true);
145 current->markDone(min, 1);
150 #if DEBUG_ACTIVE_SPANS
151 DebugShowActiveSpans(contourList);
157 // FIXME : add this as a member of SkPath
158 bool Simplify(const SkPath& path, SkPath* result) {
159 #if DEBUG_SORT || DEBUG_SWAP_TOP
160 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
162 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
163 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
164 : SkPath::kEvenOdd_FillType;
166 // turn path into list of segments
167 SkTArray<SkOpContour> contours;
168 SkOpEdgeBuilder builder(path, contours);
169 if (!builder.finish()) {
172 SkTArray<SkOpContour*, true> contourList;
173 MakeContourList(contours, contourList, false, false);
174 SkOpContour** currentPtr = contourList.begin();
176 result->setFillType(fillType);
180 SkOpContour** listEnd = contourList.end();
181 // find all intersections between segments
183 SkOpContour** nextPtr = currentPtr;
184 SkOpContour* current = *currentPtr++;
185 if (current->containsCubics()) {
186 AddSelfIntersectTs(current);
191 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
192 } while (currentPtr != listEnd);
193 if (!HandleCoincidence(&contourList, 0)) {
196 // construct closed contours
197 SkPathWriter simple(*result);
198 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
199 : !bridgeXor(contourList, &simple))
200 { // if some edges could not be resolved, assemble remaining fragments
202 temp.setFillType(fillType);
203 SkPathWriter assembled(temp);
204 Assemble(simple, &assembled);
205 *result = *assembled.nativePath();
206 result->setFillType(fillType);