blob: 57090ac4238bd658cf8bdc60c4cbee8113679ad4 [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
reed0dc4dd62015-03-24 13:55:33 -070012static 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 {
reed0dc4dd62015-03-24 13:55:33 -070020 int index, endIndex;
caryclark@google.com07393ca2013-04-08 11:47:37 +000021 bool topDone;
caryclarkdac1d172014-06-17 05:15:38 -070022 bool onlyVertical = false;
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000023 lastTopLeft = topLeft;
reed0dc4dd62015-03-24 13:55:33 -070024 SkOpSegment* current = FindSortableTop(contourList, SkOpAngle::kUnaryWinding, &firstContour,
25 &index, &endIndex, &topLeft, &topUnsortable, &topDone, &onlyVertical, firstPass);
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 if (!current) {
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000027 if ((!topUnsortable || firstPass) && !topDone) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000028 SkASSERT(topLeft.fX != SK_ScalarMin && topLeft.fY != SK_ScalarMin);
29 topLeft.fX = topLeft.fY = SK_ScalarMin;
30 continue;
31 }
32 break;
caryclarkdac1d172014-06-17 05:15:38 -070033 } else if (onlyVertical) {
34 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000035 }
commit-bot@chromium.org8cb1daa2014-04-25 12:59:11 +000036 firstPass = !topUnsortable || lastTopLeft != topLeft;
reed0dc4dd62015-03-24 13:55:33 -070037 SkTDArray<SkOpSpan*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000038 do {
reed0dc4dd62015-03-24 13:55:33 -070039 if (current->activeWinding(index, endIndex)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000040 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +000041 if (!unsortable && current->done()) {
caryclarkdac1d172014-06-17 05:15:38 -070042 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +000043 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000044 SkASSERT(unsortable || !current->done());
reed0dc4dd62015-03-24 13:55:33 -070045 int nextStart = index;
46 int nextEnd = endIndex;
caryclarkdac1d172014-06-17 05:15:38 -070047 SkOpSegment* next = current->findNextWinding(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +000048 &unsortable);
49 if (!next) {
50 if (!unsortable && simple->hasMove()
51 && current->verb() != SkPath::kLine_Verb
52 && !simple->isClosed()) {
reed0dc4dd62015-03-24 13:55:33 -070053 current->addCurveTo(index, endIndex, simple, true);
54 SkASSERT(simple->isClosed());
caryclark@google.com07393ca2013-04-08 11:47:37 +000055 }
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__,
reed0dc4dd62015-03-24 13:55:33 -070060 current->debugID(), current->xyAtT(index).fX, current->xyAtT(index).fY,
61 current->xyAtT(endIndex).fX, current->xyAtT(endIndex).fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +000062 #endif
reed0dc4dd62015-03-24 13:55:33 -070063 current->addCurveTo(index, endIndex, simple, true);
caryclark@google.com07393ca2013-04-08 11:47:37 +000064 current = next;
reed0dc4dd62015-03-24 13:55:33 -070065 index = nextStart;
66 endIndex = nextEnd;
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);
caryclark@google.com07393ca2013-04-08 11:47:37 +000075 }
76 }
77 simple->close();
78 } else {
reed0dc4dd62015-03-24 13:55:33 -070079 SkOpSpan* last = current->markAndChaseDoneUnary(index, endIndex);
80 if (last && !last->fChased && !last->fLoop) {
81 last->fChased = true;
caryclarkdac1d172014-06-17 05:15:38 -070082 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000083 // assert that last isn't already in array
caryclarkdac1d172014-06-17 05:15:38 -070084 *chase.append() = last;
85#if DEBUG_WINDING
reed0dc4dd62015-03-24 13:55:33 -070086 SkDebugf("%s chase.append id=%d windSum=%d small=%d\n", __FUNCTION__,
87 last->fOther->span(last->fOtherIndex).fOther->debugID(), last->fWindSum,
88 last->fSmall);
caryclarkdac1d172014-06-17 05:15:38 -070089#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 }
91 }
reed0dc4dd62015-03-24 13:55:33 -070092 current = FindChase(&chase, &index, &endIndex);
caryclark@google.com07393ca2013-04-08 11:47:37 +000093 #if DEBUG_ACTIVE_SPANS
94 DebugShowActiveSpans(contourList);
95 #endif
96 if (!current) {
97 break;
98 }
99 } while (true);
100 } while (true);
101 return simple->someAssemblyRequired();
102}
103
104// returns true if all edges were processed
reed0dc4dd62015-03-24 13:55:33 -0700105static bool bridgeXor(SkTArray<SkOpContour*, true>& contourList, SkPathWriter* simple) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000106 SkOpSegment* current;
reed0dc4dd62015-03-24 13:55:33 -0700107 int start, end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000108 bool unsortable = false;
109 bool closable = true;
110 while ((current = FindUndone(contourList, &start, &end))) {
111 do {
112 #if DEBUG_ACTIVE_SPANS
113 if (!unsortable && current->done()) {
114 DebugShowActiveSpans(contourList);
115 }
116 #endif
117 SkASSERT(unsortable || !current->done());
reed0dc4dd62015-03-24 13:55:33 -0700118 int nextStart = start;
119 int nextEnd = end;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 SkOpSegment* next = current->findNextXor(&nextStart, &nextEnd, &unsortable);
121 if (!next) {
122 if (!unsortable && simple->hasMove()
123 && current->verb() != SkPath::kLine_Verb
124 && !simple->isClosed()) {
125 current->addCurveTo(start, end, simple, true);
reed0dc4dd62015-03-24 13:55:33 -0700126 SkASSERT(simple->isClosed());
caryclark@google.com07393ca2013-04-08 11:47:37 +0000127 }
128 break;
129 }
130 #if DEBUG_FLOW
131 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
reed0dc4dd62015-03-24 13:55:33 -0700132 current->debugID(), current->xyAtT(start).fX, current->xyAtT(start).fY,
133 current->xyAtT(end).fX, current->xyAtT(end).fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000134 #endif
135 current->addCurveTo(start, end, simple, true);
136 current = next;
137 start = nextStart;
138 end = nextEnd;
reed0dc4dd62015-03-24 13:55:33 -0700139 } while (!simple->isClosed() && (!unsortable || !current->done(SkMin32(start, end))));
caryclark@google.com07393ca2013-04-08 11:47:37 +0000140 if (!simple->isClosed()) {
141 SkASSERT(unsortable);
reed0dc4dd62015-03-24 13:55:33 -0700142 int min = SkMin32(start, end);
143 if (!current->done(min)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 current->addCurveTo(start, end, simple, true);
reed0dc4dd62015-03-24 13:55:33 -0700145 current->markDone(min, 1);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000146 }
147 closable = false;
148 }
149 simple->close();
150 #if DEBUG_ACTIVE_SPANS
151 DebugShowActiveSpans(contourList);
152 #endif
153 }
154 return closable;
155}
156
157// FIXME : add this as a member of SkPath
caryclark@google.com66560ca2013-04-26 19:51:16 +0000158bool Simplify(const SkPath& path, SkPath* result) {
caryclarkccec0f92015-03-24 07:28:17 -0700159#if DEBUG_SORT || DEBUG_SWAP_TOP
160 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
161#endif
reed0dc4dd62015-03-24 13:55:33 -0700162 // returns 1 for evenodd, -1 for winding, regardless of inverse-ness
163 SkPath::FillType fillType = path.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
164 : SkPath::kEvenOdd_FillType;
165
166 // turn path into list of segments
167 SkTArray<SkOpContour> contours;
168 SkOpEdgeBuilder builder(path, contours);
169 if (!builder.finish()) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000170 return false;
171 }
reed0dc4dd62015-03-24 13:55:33 -0700172 SkTArray<SkOpContour*, true> contourList;
173 MakeContourList(contours, contourList, false, false);
174 SkOpContour** currentPtr = contourList.begin();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000175 result->reset();
bungeman@google.coma5809a32013-06-21 15:13:34 +0000176 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000177 if (!currentPtr) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000178 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000179 }
reed0dc4dd62015-03-24 13:55:33 -0700180 SkOpContour** listEnd = contourList.end();
caryclark@google.com07393ca2013-04-08 11:47:37 +0000181 // find all intersections between segments
182 do {
183 SkOpContour** nextPtr = currentPtr;
184 SkOpContour* current = *currentPtr++;
reed0dc4dd62015-03-24 13:55:33 -0700185 if (current->containsCubics()) {
186 AddSelfIntersectTs(current);
187 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000188 SkOpContour* next;
189 do {
190 next = *nextPtr++;
reed0dc4dd62015-03-24 13:55:33 -0700191 } while (AddIntersectTs(current, next) && nextPtr != listEnd);
192 } while (currentPtr != listEnd);
193 if (!HandleCoincidence(&contourList, 0)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000194 return false;
195 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000196 // construct closed contours
reed0dc4dd62015-03-24 13:55:33 -0700197 SkPathWriter simple(*result);
198 if (builder.xorMask() == kWinding_PathOpsMask ? bridgeWinding(contourList, &simple)
199 : !bridgeXor(contourList, &simple))
caryclark@google.com07393ca2013-04-08 11:47:37 +0000200 { // if some edges could not be resolved, assemble remaining fragments
201 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000202 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000203 SkPathWriter assembled(temp);
reed0dc4dd62015-03-24 13:55:33 -0700204 Assemble(simple, &assembled);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000205 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000206 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000207 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000208 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000209}