blob: 257cb8ee69b646bc127825e2a82c518794750137 [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;
halcanary96fcdcc2015-08-27 07:41:13 -070022 *endPtr = nullptr;
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 }
halcanary96fcdcc2015-08-27 07:41:13 -070051 SkOpSegment* first = nullptr;
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 }
halcanary96fcdcc2015-08-27 07:41:13 -070084 return nullptr;
caryclark@google.com07393ca2013-04-08 11:47:37 +000085}
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()) {
caryclarkef784fb2015-10-30 12:03:06 -0700114 if (!current->addCurveTo(start, end, simple)) {
115 return false;
116 }
caryclarkdac1d172014-06-17 05:15:38 -0700117 #if DEBUG_ACTIVE_SPANS
118 if (!simple->isClosed()) {
119 DebugShowActiveSpans(contourList);
120 }
121 #endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000122 }
123 break;
124 }
125 #if DEBUG_FLOW
caryclark54359292015-03-26 07:52:43 -0700126 SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
127 current->debugID(), start->pt().fX, start->pt().fY,
128 end->pt().fX, end->pt().fY);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000129 #endif
caryclarkef784fb2015-10-30 12:03:06 -0700130 if (!current->addCurveTo(start, end, simple)) {
131 return false;
132 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000133 current = next;
caryclark54359292015-03-26 07:52:43 -0700134 start = nextStart;
135 end = nextEnd;
136 } while (!simple->isClosed() && (!unsortable || !start->starter(end)->done()));
137 if (current->activeWinding(start, end) && !simple->isClosed()) {
138 SkOpSpan* spanStart = start->starter(end);
139 if (!spanStart->done()) {
caryclarkef784fb2015-10-30 12:03:06 -0700140 if (!current->addCurveTo(start, end, simple)) {
141 return false;
142 }
caryclark54359292015-03-26 07:52:43 -0700143 current->markDone(spanStart);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000144 }
145 }
146 simple->close();
147 } else {
caryclark54359292015-03-26 07:52:43 -0700148 SkOpSpanBase* last = current->markAndChaseDone(start, end);
149 if (last && !last->chased()) {
150 last->setChased(true);
caryclarkdac1d172014-06-17 05:15:38 -0700151 SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
152 *chase.append() = last;
153#if DEBUG_WINDING
caryclark54359292015-03-26 07:52:43 -0700154 SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
155 if (!last->final()) {
156 SkDebugf(" windSum=%d", last->upCast()->windSum());
157 }
158 SkDebugf("\n");
caryclarkdac1d172014-06-17 05:15:38 -0700159#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000160 }
161 }
caryclark54359292015-03-26 07:52:43 -0700162 current = findChaseOp(chase, &start, &end);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000163 #if DEBUG_ACTIVE_SPANS
164 DebugShowActiveSpans(contourList);
165 #endif
166 if (!current) {
167 break;
168 }
169 } while (true);
170 } while (true);
171 return simple->someAssemblyRequired();
172}
173
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000174// pretty picture:
175// https://docs.google.com/a/google.com/drawings/d/1sPV8rPfpEFXymBp3iSbDRWAycp1b-7vD9JP2V-kn9Ss/edit?usp=sharing
caryclark54359292015-03-26 07:52:43 -0700176static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000177// inside minuend outside minuend
178// inside subtrahend outside subtrahend inside subtrahend outside subtrahend
caryclark54359292015-03-26 07:52:43 -0700179{{ kDifference_SkPathOp, kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
180{{ kIntersect_SkPathOp, kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
181{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp, kIntersect_SkPathOp }},
182{{ kXOR_SkPathOp, kXOR_SkPathOp }, { kXOR_SkPathOp, kXOR_SkPathOp }},
183{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp, kDifference_SkPathOp }},
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000184};
185
caryclark54359292015-03-26 07:52:43 -0700186static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000187 {{ false, false }, { true, false }}, // diff
188 {{ false, false }, { false, true }}, // sect
189 {{ false, true }, { true, true }}, // union
190 {{ false, true }, { true, false }}, // xor
191 {{ false, true }, { false, false }}, // rev diff
192};
193
caryclark65f55312014-11-13 06:58:52 -0800194#define DEBUGGING_PATHOPS_FROM_HOST 0 // enable to debug svg in chrome -- note path hardcoded below
195#if DEBUGGING_PATHOPS_FROM_HOST
196#include "SkData.h"
197#include "SkStream.h"
198
199static void dump_path(FILE* file, const SkPath& path, bool force, bool dumpAsHex) {
200 SkDynamicMemoryWStream wStream;
201 path.dump(&wStream, force, dumpAsHex);
202 SkAutoDataUnref data(wStream.copyToData());
203 fprintf(file, "%.*s\n", (int) data->size(), data->data());
204}
205
206static int dumpID = 0;
207
208static void dump_op(const SkPath& one, const SkPath& two, SkPathOp op) {
209#if SK_BUILD_FOR_MAC
210 FILE* file = fopen("/Users/caryclark/Documents/svgop.txt", "w");
211#else
212 FILE* file = fopen("/usr/local/google/home/caryclark/Documents/svgop.txt", "w");
213#endif
214 fprintf(file,
215 "\nstatic void fuzz763_%d(skiatest::Reporter* reporter, const char* filename) {\n",
216 ++dumpID);
217 fprintf(file, " SkPath path;\n");
218 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", one.getFillType());
219 dump_path(file, one, false, true);
220 fprintf(file, " SkPath path1(path);\n");
221 fprintf(file, " path.reset();\n");
222 fprintf(file, " path.setFillType((SkPath::FillType) %d);\n", two.getFillType());
223 dump_path(file, two, false, true);
224 fprintf(file, " SkPath path2(path);\n");
225 fprintf(file, " testPathOp(reporter, path1, path2, (SkPathOp) %d, filename);\n", op);
caryclark54359292015-03-26 07:52:43 -0700226 fprintf(file, "}\n");
caryclark65f55312014-11-13 06:58:52 -0800227 fclose(file);
228}
229#endif
230
caryclark26ad22a2015-10-16 09:03:38 -0700231
232#if DEBUG_T_SECT_LOOP_COUNT
233
234#include "SkMutex.h"
235
236SK_DECLARE_STATIC_MUTEX(debugWorstLoop);
237
238SkOpGlobalState debugWorstState(nullptr, nullptr SkDEBUGPARAMS(nullptr));
239
240void ReportPathOpsDebugging() {
241 debugWorstState.debugLoopReport();
242}
243
244extern void (*gVerboseFinalize)();
245
246#endif
247
caryclark624637c2015-05-11 07:21:27 -0700248bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result,
caryclarkd4349722015-07-23 12:40:22 -0700249 bool expectSuccess SkDEBUGPARAMS(const char* testName)) {
caryclark54359292015-03-26 07:52:43 -0700250 SkChunkAlloc allocator(4096); // FIXME: add a constant expression here, tune
251 SkOpContour contour;
caryclark624637c2015-05-11 07:21:27 -0700252 SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
caryclark54359292015-03-26 07:52:43 -0700253 SkOpCoincidence coincidence;
caryclarkd4349722015-07-23 12:40:22 -0700254 SkOpGlobalState globalState(&coincidence, contourList SkDEBUGPARAMS(testName));
caryclark65f55312014-11-13 06:58:52 -0800255#if DEBUGGING_PATHOPS_FROM_HOST
256 dump_op(one, two, op);
caryclark54359292015-03-26 07:52:43 -0700257#endif
258#if 0 && DEBUG_SHOW_TEST_NAME
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000259 char* debugName = DEBUG_FILENAME_STRING;
260 if (debugName && debugName[0]) {
caryclark@google.com570863f2013-09-16 15:55:01 +0000261 SkPathOpsDebug::BumpTestName(debugName);
262 SkPathOpsDebug::ShowPath(one, two, op, debugName);
caryclark@google.com07e97fc2013-07-08 17:17:02 +0000263 }
caryclark@google.coma5e55922013-05-07 18:51:31 +0000264#endif
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000265 op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000266 SkPath::FillType fillType = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()]
267 ? SkPath::kInverseEvenOdd_FillType : SkPath::kEvenOdd_FillType;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000268 const SkPath* minuend = &one;
269 const SkPath* subtrahend = &two;
caryclark54359292015-03-26 07:52:43 -0700270 if (op == kReverseDifference_SkPathOp) {
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000271 minuend = &two;
272 subtrahend = &one;
caryclark54359292015-03-26 07:52:43 -0700273 op = kDifference_SkPathOp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000274 }
caryclark624637c2015-05-11 07:21:27 -0700275#if DEBUG_SORT
caryclark@google.com570863f2013-09-16 15:55:01 +0000276 SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000277#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000278 // turn path into list of segments
caryclark54359292015-03-26 07:52:43 -0700279 SkOpEdgeBuilder builder(*minuend, &contour, &allocator, &globalState);
caryclarkd751ac02014-10-03 05:36:27 -0700280 if (builder.unparseable()) {
281 return false;
282 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000283 const int xorMask = builder.xorMask();
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000284 builder.addOperand(*subtrahend);
caryclark54359292015-03-26 07:52:43 -0700285 if (!builder.finish(&allocator)) {
caryclark@google.com66560ca2013-04-26 19:51:16 +0000286 return false;
287 }
caryclark03b03ca2015-04-23 09:13:37 -0700288#if DEBUG_DUMP_SEGMENTS
caryclark26ad22a2015-10-16 09:03:38 -0700289 contourList->dumpSegments("seg", op);
caryclark54359292015-03-26 07:52:43 -0700290#endif
291
caryclark@google.com07393ca2013-04-08 11:47:37 +0000292 const int xorOpMask = builder.xorMask();
caryclark624637c2015-05-11 07:21:27 -0700293 if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
294 xorOpMask == kEvenOdd_PathOpsMask)) {
caryclark1049f122015-04-20 08:31:59 -0700295 result->reset();
296 result->setFillType(fillType);
caryclark@google.com66560ca2013-04-26 19:51:16 +0000297 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000298 }
caryclark@google.com07393ca2013-04-08 11:47:37 +0000299 // find all intersections between segments
caryclark624637c2015-05-11 07:21:27 -0700300 SkOpContour* current = contourList;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000301 do {
caryclark624637c2015-05-11 07:21:27 -0700302 SkOpContour* next = current;
303 while (AddIntersectTs(current, next, &coincidence, &allocator)
304 && (next = next->next()))
305 ;
306 } while ((current = current->next()));
caryclark54359292015-03-26 07:52:43 -0700307#if DEBUG_VALIDATE
308 globalState.setPhase(SkOpGlobalState::kWalking);
309#endif
caryclark624637c2015-05-11 07:21:27 -0700310 if (!HandleCoincidence(contourList, &coincidence, &allocator)) {
commit-bot@chromium.org4431e772014-04-14 17:08:59 +0000311 return false;
312 }
caryclark26ad22a2015-10-16 09:03:38 -0700313#if DEBUG_ALIGNMENT
314 contourList->dumpSegments("aligned");
315#endif
caryclark@google.com07393ca2013-04-08 11:47:37 +0000316 // construct closed contours
caryclark1049f122015-04-20 08:31:59 -0700317 result->reset();
318 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000319 SkPathWriter wrapper(*result);
caryclark54359292015-03-26 07:52:43 -0700320 bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper, &allocator);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000321 { // if some edges could not be resolved, assemble remaining fragments
322 SkPath temp;
caryclark@google.com7dfbb072013-04-22 14:37:05 +0000323 temp.setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000324 SkPathWriter assembled(temp);
325 Assemble(wrapper, &assembled);
326 *result = *assembled.nativePath();
caryclark@google.com66560ca2013-04-26 19:51:16 +0000327 result->setFillType(fillType);
caryclark@google.com07393ca2013-04-08 11:47:37 +0000328 }
caryclark26ad22a2015-10-16 09:03:38 -0700329#if DEBUG_T_SECT_LOOP_COUNT
330 {
331 SkAutoMutexAcquire autoM(debugWorstLoop);
332 if (!gVerboseFinalize) {
333 gVerboseFinalize = &ReportPathOpsDebugging;
334 }
335 debugWorstState.debugDoYourWorst(&globalState);
336 }
337#endif
caryclark@google.com66560ca2013-04-26 19:51:16 +0000338 return true;
caryclark@google.com07393ca2013-04-08 11:47:37 +0000339}
caryclark624637c2015-05-11 07:21:27 -0700340
caryclark26ad22a2015-10-16 09:03:38 -0700341#define DEBUG_VERIFY 0
342
343#if DEBUG_VERIFY
344#include "SkBitmap.h"
345#include "SkCanvas.h"
346#include "SkPaint.h"
347
348const int bitWidth = 64;
349const int bitHeight = 64;
350
351static void debug_scale_matrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
352 SkRect larger = one.getBounds();
353 larger.join(two.getBounds());
354 SkScalar largerWidth = larger.width();
355 if (largerWidth < 4) {
356 largerWidth = 4;
357 }
358 SkScalar largerHeight = larger.height();
359 if (largerHeight < 4) {
360 largerHeight = 4;
361 }
362 SkScalar hScale = (bitWidth - 2) / largerWidth;
363 SkScalar vScale = (bitHeight - 2) / largerHeight;
364 scale.reset();
365 scale.preScale(hScale, vScale);
366 larger.fLeft *= hScale;
367 larger.fRight *= hScale;
368 larger.fTop *= vScale;
369 larger.fBottom *= vScale;
370 SkScalar dx = -16000 > larger.fLeft ? -16000 - larger.fLeft
371 : 16000 < larger.fRight ? 16000 - larger.fRight : 0;
372 SkScalar dy = -16000 > larger.fTop ? -16000 - larger.fTop
373 : 16000 < larger.fBottom ? 16000 - larger.fBottom : 0;
374 scale.preTranslate(dx, dy);
375}
376
377static int debug_paths_draw_the_same(const SkPath& one, const SkPath& two, SkBitmap& bits) {
378 if (bits.width() == 0) {
379 bits.allocN32Pixels(bitWidth * 2, bitHeight);
380 }
381 SkCanvas canvas(bits);
382 canvas.drawColor(SK_ColorWHITE);
383 SkPaint paint;
384 canvas.save();
385 const SkRect& bounds1 = one.getBounds();
386 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
387 canvas.drawPath(one, paint);
388 canvas.restore();
389 canvas.save();
390 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
391 canvas.drawPath(two, paint);
392 canvas.restore();
393 int errors = 0;
394 for (int y = 0; y < bitHeight - 1; ++y) {
395 uint32_t* addr1 = bits.getAddr32(0, y);
396 uint32_t* addr2 = bits.getAddr32(0, y + 1);
397 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
398 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
399 for (int x = 0; x < bitWidth - 1; ++x) {
400 // count 2x2 blocks
401 bool err = addr1[x] != addr3[x];
402 if (err) {
403 errors += addr1[x + 1] != addr3[x + 1]
404 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
405 }
406 }
407 }
408 return errors;
409}
410
411#endif
412
caryclark624637c2015-05-11 07:21:27 -0700413bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
caryclark26ad22a2015-10-16 09:03:38 -0700414#if DEBUG_VERIFY
415 if (!OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr))) {
416 SkDebugf("%s did not expect failure\none: fill=%d\n", __FUNCTION__, one.getFillType());
417 one.dumpHex();
418 SkDebugf("two: fill=%d\n", two.getFillType());
419 two.dumpHex();
420 SkASSERT(0);
421 return false;
422 }
423 SkPath pathOut, scaledPathOut;
424 SkRegion rgnA, rgnB, openClip, rgnOut;
425 openClip.setRect(-16000, -16000, 16000, 16000);
426 rgnA.setPath(one, openClip);
427 rgnB.setPath(two, openClip);
428 rgnOut.op(rgnA, rgnB, (SkRegion::Op) op);
429 rgnOut.getBoundaryPath(&pathOut);
430 SkMatrix scale;
431 debug_scale_matrix(one, two, scale);
432 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
433 SkPath scaledA, scaledB;
434 scaledA.addPath(one, scale);
435 scaledA.setFillType(one.getFillType());
436 scaledB.addPath(two, scale);
437 scaledB.setFillType(two.getFillType());
438 scaledRgnA.setPath(scaledA, openClip);
439 scaledRgnB.setPath(scaledB, openClip);
440 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) op);
441 scaledRgnOut.getBoundaryPath(&scaledPathOut);
442 SkBitmap bitmap;
443 SkPath scaledOut;
444 scaledOut.addPath(*result, scale);
445 scaledOut.setFillType(result->getFillType());
446 int errors = debug_paths_draw_the_same(scaledPathOut, scaledOut, bitmap);
447 const int MAX_ERRORS = 9;
448 if (errors > MAX_ERRORS) {
449 SkDebugf("%s did not expect failure\none: fill=%d\n", __FUNCTION__, one.getFillType());
450 one.dumpHex();
451 SkDebugf("two: fill=%d\n", two.getFillType());
452 two.dumpHex();
453 SkASSERT(0);
454 }
455 return true;
456#else
halcanary96fcdcc2015-08-27 07:41:13 -0700457 return OpDebug(one, two, op, result, true SkDEBUGPARAMS(nullptr));
caryclark26ad22a2015-10-16 09:03:38 -0700458#endif
caryclark624637c2015-05-11 07:21:27 -0700459}