blob: 9c09eaf8f3fcbcfbee8af15a01f9beadda62c2a7 [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
caryclark54359292015-03-26 07:52:43 -070013static SkOpSegment* findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
14 SkOpSpanBase** endPtr) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000015 while (chase.count()) {
caryclark54359292015-03-26 07:52:43 -070016 SkOpSpanBase* span;
caryclark@google.com07393ca2013-04-08 11:47:37 +000017 chase.pop(&span);
caryclark54359292015-03-26 07:52:43 -070018 // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
19 *startPtr = span->ptT()->prev()->span();
20 SkOpSegment* segment = (*startPtr)->segment();
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000021 bool done = true;
caryclark54359292015-03-26 07:52:43 -070022 *endPtr = NULL;
caryclarkbca19f72015-05-13 08:23:48 -070023 if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
caryclark54359292015-03-26 07:52:43 -070024 *startPtr = last->start();
25 *endPtr = last->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000026 #if TRY_ROTATE
27 *chase.insert(0) = span;
28 #else
29 *chase.append() = span;
30 #endif
31 return last->segment();
32 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000033 if (done) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000034 continue;
35 }
caryclarkbca19f72015-05-13 08:23:48 -070036 int winding;
37 bool sortable;
38 const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
caryclark65f55312014-11-13 06:58:52 -080039 if (winding == SK_MinS32) {
40 continue;
41 }
caryclarkbca19f72015-05-13 08:23:48 -070042 int sumMiWinding, sumSuWinding;
43 if (sortable) {
44 segment = angle->segment();
45 sumMiWinding = segment->updateWindingReverse(angle);
46 sumSuWinding = segment->updateOppWindingReverse(angle);
47 if (segment->operand()) {
48 SkTSwap<int>(sumMiWinding, sumSuWinding);
49 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000050 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000051 SkOpSegment* first = NULL;
caryclarkbca19f72015-05-13 08:23:48 -070052 const SkOpAngle* firstAngle = angle;
caryclark54359292015-03-26 07:52:43 -070053 while ((angle = angle->next()) != firstAngle) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000054 segment = angle->segment();
caryclark54359292015-03-26 07:52:43 -070055 SkOpSpanBase* start = angle->start();
56 SkOpSpanBase* end = angle->end();
caryclark@google.com07393ca2013-04-08 11:47:37 +000057 int maxWinding, sumWinding, oppMaxWinding, oppSumWinding;
caryclarkbca19f72015-05-13 08:23:48 -070058 if (sortable) {
59 segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
60 &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
61 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000062 if (!segment->done(angle)) {
caryclarkbca19f72015-05-13 08:23:48 -070063 if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000064 first = segment;
caryclark54359292015-03-26 07:52:43 -070065 *startPtr = start;
66 *endPtr = end;
caryclark65f55312014-11-13 06:58:52 -080067 }
caryclarkdac1d172014-06-17 05:15:38 -070068 // OPTIMIZATION: should this also add to the chase?
caryclarkbca19f72015-05-13 08:23:48 -070069 if (sortable) {
70 (void) segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
71 oppSumWinding, angle);
72 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000073 }
commit-bot@chromium.org4431e772014-04-14 17:08:59 +000074 }
caryclark@google.com07393ca2013-04-08 11:47:37 +000075 if (first) {
76 #if TRY_ROTATE
77 *chase.insert(0) = span;
78 #else
79 *chase.append() = span;
80 #endif
81 return first;
82 }
83 }
84 return NULL;
85}
86
caryclark624637c2015-05-11 07:21:27 -070087static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
caryclark54359292015-03-26 07:52:43 -070088 const int xorMask, const int xorOpMask, SkPathWriter* simple, SkChunkAlloc* allocator) {
caryclark@google.com07393ca2013-04-08 11:47:37 +000089 bool unsortable = false;
caryclark@google.com07393ca2013-04-08 11:47:37 +000090 do {
caryclark624637c2015-05-11 07:21:27 -070091 SkOpSpan* span = FindSortableTop(contourList);
92 if (!span) {
caryclarkdac1d172014-06-17 05:15:38 -070093 break;
caryclark@google.com07393ca2013-04-08 11:47:37 +000094 }
caryclark624637c2015-05-11 07:21:27 -070095 SkOpSegment* current = span->segment();
96 SkOpSpanBase* start = span->next();
97 SkOpSpanBase* end = span;
caryclark54359292015-03-26 07:52:43 -070098 SkTDArray<SkOpSpanBase*> chase;
caryclark@google.com07393ca2013-04-08 11:47:37 +000099 do {
caryclark54359292015-03-26 07:52:43 -0700100 if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000101 do {
caryclark@google.com07393ca2013-04-08 11:47:37 +0000102 if (!unsortable && current->done()) {
caryclark@google.com7eaa53d2013-10-02 14:49:34 +0000103 break;
caryclark@google.coma5e55922013-05-07 18:51:31 +0000104 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000105 SkASSERT(unsortable || !current->done());
caryclark54359292015-03-26 07:52:43 -0700106 SkOpSpanBase* nextStart = start;
107 SkOpSpanBase* nextEnd = end;
caryclarkdac1d172014-06-17 05:15:38 -0700108 SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
caryclark@google.com07393ca2013-04-08 11:47:37 +0000109 &unsortable, op, xorMask, xorOpMask);
110 if (!next) {
111 if (!unsortable && simple->hasMove()
112 && current->verb() != SkPath::kLine_Verb
113 && !simple->isClosed()) {
caryclark54359292015-03-26 07:52:43 -0700114 current->addCurveTo(start, end, simple, true);
caryclarkdac1d172014-06-17 05:15:38 -0700115 #if DEBUG_ACTIVE_SPANS
116 if (!simple->isClosed()) {
117 DebugShowActiveSpans(contourList);
118 }
119 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000120 }
121 break;
122 }
123 #if DEBUG_FLOW
caryclark54359292015-03-26 07:52:43 -0700124 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
125 current->debugID(), start->pt().fX, start->pt().fY,
126 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000127 #endif
caryclark54359292015-03-26 07:52:43 -0700128 current->addCurveTo(start, end, simple, true);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000129 current = next;
caryclark54359292015-03-26 07:52:43 -0700130 start = nextStart;
131 end = nextEnd;
132 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
133 if (current->activeWinding(start, end) && !simple->isClosed()) {
134 SkOpSpan* spanStart = start->starter(end);
135 if (!spanStart->done()) {
136 current->addCurveTo(start, end, simple, true);
137 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000138 }
139 }
140 simple->close();
141 } else {
caryclark54359292015-03-26 07:52:43 -0700142 SkOpSpanBase* last = current->markAndChaseDone(start, end);
143 if (last && !last->chased()) {
144 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -0700145 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
146 *chase.append() = last;
147#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700148 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
149 if (!last->final()) {
150 SkDebugf(" windSum=%d", last->upCast()->windSum());
151 }
152 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700153#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000154 }
155 }
caryclark54359292015-03-26 07:52:43 -0700156 current = findChaseOp(chase, &start, &end);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000157 #if DEBUG_ACTIVE_SPANS
158 DebugShowActiveSpans(contourList);
159 #endif
160 if (!current) {
161 break;
162 }
163 } while (true);
164 } while (true);
165 return simple->someAssemblyRequired();
166}
167
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000168// pretty picture:
169// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
caryclark54359292015-03-26 07:52:43 -0700170static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000171// inside minuend outside minuend
172// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
caryclark54359292015-03-26 07:52:43 -0700173{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
174{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
175{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
176{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
177{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000178};
179
caryclark54359292015-03-26 07:52:43 -0700180static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000181 {{ false, false }, { true, false }}, // diff
182 {{ false, false }, { false, true }}, // sect
183 {{ false, true }, { true, true }}, // union
184 {{ false, true }, { true, false }}, // xor
185 {{ false, true }, { false, false }}, // rev diff
186};
187
caryclark65f55312014-11-13 06:58:52 -0800188#define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note path hardcoded below
189#if DEBUGGING_PATHOPS_FROM_HOST
190#include "SkData.h"
191#include "SkStream.h"
192
193static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
194 SkDynamicMemoryWStream wStream;
195 path.dump(&wStream, force, dumpAsHex);
196 SkAutoDataUnref data(wStream.copyToData());
197 fprintf(file, "%.*s\n", (int) data->size(), data->data());
198}
199
200static int dumpID = 0;
201
202static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
203#if SK_BUILD_FOR_MAC
204 FILE* file = fopen("/Users/caryclark/Documents/svgop.txt", "w");
205#else
206 FILE* file = fopen("/usr/local/google/home/caryclark/Documents/svgop.txt", "w");
207#endif
208 fprintf(file,
209 "\nstatic void fuzz763_%d(skiatest::Reporter* reporter, const char* filename) {\n",
210 ++dumpID);
211 fprintf(file, " SkPath path;\n");
212 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
213 dump_path(file, one, false, true);
214 fprintf(file, " SkPath path1(path);\n");
215 fprintf(file, " path.reset();\n");
216 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
217 dump_path(file, two, false, true);
218 fprintf(file, " SkPath path2(path);\n");
219 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
caryclark54359292015-03-26 07:52:43 -0700220 fprintf(file, "}\n");
caryclark65f55312014-11-13 06:58:52 -0800221 fclose(file);
222}
223#endif
224
caryclark624637c2015-05-11 07:21:27 -0700225bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
226 bool expectSuccess) {
caryclark54359292015-03-26 07:52:43 -0700227 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
228 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700229 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark54359292015-03-26 07:52:43 -0700230 SkOpCoincidence coincidence;
caryclark624637c2015-05-11 07:21:27 -0700231 SkOpGlobalState globalState(&coincidence, contourList);
caryclark65f55312014-11-13 06:58:52 -0800232#if DEBUGGING_PATHOPS_FROM_HOST
233 dump_op(one, two, op);
caryclark54359292015-03-26 07:52:43 -0700234#endif
235#if 0 && DEBUG_SHOW_TEST_NAME
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000236 char* debugName = DEBUG_FILENAME_STRING;
237 if (debugName && debugName[0]) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000238 SkPathOpsDebug::BumpTestName(debugName);
239 SkPathOpsDebug::ShowPath(one, two, op, debugName);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000240 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000241#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000242 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000243 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
244 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000245 const SkPath* minuend = &one;
246 const SkPath* subtrahend = &two;
caryclark54359292015-03-26 07:52:43 -0700247 if (op == kReverseDifference_SkPathOp) {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000248 minuend = &two;
249 subtrahend = &one;
caryclark54359292015-03-26 07:52:43 -0700250 op = kDifference_SkPathOp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000251 }
caryclark624637c2015-05-11 07:21:27 -0700252#if DEBUG_SORT
caryclark@google.com570863f2013-09-16 15:55:01 +0000253 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000254#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000255 // turn path into list of segments
caryclark54359292015-03-26 07:52:43 -0700256 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
caryclarkd751ac02014-10-03 05:36:27 -0700257 if (builder.unparseable()) {
258 return false;
259 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000260 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000261 builder.addOperand(*subtrahend);
caryclark54359292015-03-26 07:52:43 -0700262 if (!builder.finish(&allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000263 return false;
264 }
caryclark03b03ca2015-04-23 09:13:37 -0700265#if DEBUG_DUMP_SEGMENTS
caryclark54359292015-03-26 07:52:43 -0700266 contour.dumpSegments(op);
267#endif
268
caryclark@google.com07393ca2013-04-08 11:47:37 +0000269 const int xorOpMask = builder.xorMask();
caryclark624637c2015-05-11 07:21:27 -0700270 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
271 xorOpMask == kEvenOdd_PathOpsMask)) {
caryclark1049f122015-04-20 08:31:59 -0700272 result->reset();
273 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000274 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000275 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000276 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700277 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278 do {
caryclark624637c2015-05-11 07:21:27 -0700279 SkOpContour* next = current;
280 while (AddIntersectTs(current, next, &coincidence, &allocator)
281 && (next = next->next()))
282 ;
283 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700284#if DEBUG_VALIDATE
285 globalState.setPhase(SkOpGlobalState::kWalking);
286#endif
caryclark624637c2015-05-11 07:21:27 -0700287 if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000288 return false;
289 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000290 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700291 result->reset();
292 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000293 SkPathWriter wrapper(*result);
caryclark54359292015-03-26 07:52:43 -0700294 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000295 { // if some edges could not be resolved, assemble remaining fragments
296 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000297 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 SkPathWriter assembled(temp);
299 Assemble(wrapper, &assembled);
300 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000301 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000302 }
caryclark@google.com66560ca2013-04-26 19:51:16 +0000303 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000304}
caryclark624637c2015-05-11 07:21:27 -0700305
306bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
307 return OpDebug(one, two, op, result, true);
308}