Coverage Report

Created: 2021-08-22 09:07

/src/skia/src/pathops/SkPathOpsOp.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
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 "src/pathops/SkAddIntersections.h"
8
#include "src/pathops/SkOpCoincidence.h"
9
#include "src/pathops/SkOpEdgeBuilder.h"
10
#include "src/pathops/SkPathOpsCommon.h"
11
#include "src/pathops/SkPathWriter.h"
12
13
#include <utility>
14
15
static bool findChaseOp(SkTDArray<SkOpSpanBase*>& chase, SkOpSpanBase** startPtr,
16
2.49M
        SkOpSpanBase** endPtr, SkOpSegment** result) {
17
6.57M
    while (chase.count()) {
18
6.17M
        SkOpSpanBase* span;
19
6.17M
        chase.pop(&span);
20
        // OPTIMIZE: prev makes this compatible with old code -- but is it necessary?
21
6.17M
        *startPtr = span->ptT()->prev()->span();
22
6.17M
        SkOpSegment* segment = (*startPtr)->segment();
23
6.17M
        bool done = true;
24
6.17M
        *endPtr = nullptr;
25
6.17M
        if (SkOpAngle* last = segment->activeAngle(*startPtr, startPtr, endPtr, &done)) {
26
1.21M
            *startPtr = last->start();
27
1.21M
            *endPtr = last->end();
28
   #if TRY_ROTATE
29
            *chase.insert(0) = span;
30
   #else
31
1.21M
            *chase.append() = span;
32
1.21M
   #endif
33
1.21M
            *result = last->segment();
34
1.21M
            return true;
35
1.21M
        }
36
4.96M
        if (done) {
37
4.07M
            continue;
38
4.07M
        }
39
893k
        int winding;
40
893k
        bool sortable;
41
893k
        const SkOpAngle* angle = AngleWinding(*startPtr, *endPtr, &winding, &sortable);
42
893k
        if (!angle) {
43
953
            *result = nullptr;
44
953
            return true;
45
953
        }
46
892k
        if (winding == SK_MinS32) {
47
116
            continue;
48
116
        }
49
892k
        int sumMiWinding, sumSuWinding;
50
892k
        if (sortable) {
51
880k
            segment = angle->segment();
52
880k
            sumMiWinding = segment->updateWindingReverse(angle);
53
880k
            if (sumMiWinding == SK_MinS32) {
54
17
                SkASSERT(segment->globalState()->debugSkipAssert());
55
17
                *result = nullptr;
56
17
                return true;
57
17
            }
58
880k
            sumSuWinding = segment->updateOppWindingReverse(angle);
59
880k
            if (sumSuWinding == SK_MinS32) {
60
0
                SkASSERT(segment->globalState()->debugSkipAssert());
61
0
                *result = nullptr;
62
0
                return true;
63
0
            }
64
880k
            if (segment->operand()) {
65
607k
                using std::swap;
66
607k
                swap(sumMiWinding, sumSuWinding);
67
607k
            }
68
880k
        }
69
892k
        SkOpSegment* first = nullptr;
70
892k
        const SkOpAngle* firstAngle = angle;
71
3.68M
        while ((angle = angle->next()) != firstAngle) {
72
2.79M
            segment = angle->segment();
73
2.79M
            SkOpSpanBase* start = angle->start();
74
2.79M
            SkOpSpanBase* end = angle->end();
75
2.79M
            int maxWinding = 0, sumWinding = 0, oppMaxWinding = 0, oppSumWinding = 0;
76
2.79M
            if (sortable) {
77
2.69M
                segment->setUpWindings(start, end, &sumMiWinding, &sumSuWinding,
78
2.69M
                        &maxWinding, &sumWinding, &oppMaxWinding, &oppSumWinding);
79
2.69M
            }
80
2.79M
            if (!segment->done(angle)) {
81
1.98M
                if (!first && (sortable || start->starter(end)->windSum() != SK_MinS32)) {
82
888k
                    first = segment;
83
888k
                    *startPtr = start;
84
888k
                    *endPtr = end;
85
888k
                }
86
                // OPTIMIZATION: should this also add to the chase?
87
1.98M
                if (sortable) {
88
1.95M
                    if (!segment->markAngle(maxWinding, sumWinding, oppMaxWinding,
89
1.05k
                            oppSumWinding, angle, nullptr)) {
90
1.05k
                        return false;
91
1.05k
                    }
92
1.95M
                }
93
1.98M
            }
94
2.79M
        }
95
891k
        if (first) {
96
       #if TRY_ROTATE
97
            *chase.insert(0) = span;
98
       #else
99
887k
            *chase.append() = span;
100
887k
       #endif
101
887k
            *result = first;
102
887k
            return true;
103
887k
        }
104
891k
    }
105
397k
    *result = nullptr;
106
397k
    return true;
107
2.49M
}
108
109
static bool bridgeOp(SkOpContourHead* contourList, const SkPathOp op,
110
172k
        const int xorMask, const int xorOpMask, SkPathWriter* writer) {
111
172k
    bool unsortable = false;
112
172k
    bool lastSimple = false;
113
172k
    bool simple = false;
114
571k
    do {
115
571k
        SkOpSpan* span = FindSortableTop(contourList);
116
571k
        if (!span) {
117
157k
            break;
118
157k
        }
119
413k
        SkOpSegment* current = span->segment();
120
413k
        SkOpSpanBase* start = span->next();
121
413k
        SkOpSpanBase* end = span;
122
413k
        SkTDArray<SkOpSpanBase*> chase;
123
2.51M
        do {
124
2.51M
            if (current->activeOp(start, end, xorMask, xorOpMask, op)) {
125
5.86M
                do {
126
5.86M
                    if (!unsortable && current->done()) {
127
163k
                        break;
128
163k
                    }
129
5.70M
                    SkASSERT(unsortable || !current->done());
130
5.70M
                    SkOpSpanBase* nextStart = start;
131
5.70M
                    SkOpSpanBase* nextEnd = end;
132
5.70M
                    lastSimple = simple;
133
5.70M
                    SkOpSegment* next = current->findNextOp(&chase, &nextStart, &nextEnd,
134
5.70M
                            &unsortable, &simple, op, xorMask, xorOpMask);
135
5.70M
                    if (!next) {
136
65.4k
                        if (!unsortable && writer->hasMove()
137
3.47k
                                && current->verb() != SkPath::kLine_Verb
138
199
                                && !writer->isClosed()) {
139
0
                            if (!current->addCurveTo(start, end, writer)) {
140
0
                                return false;
141
0
                            }
142
0
                            if (!writer->isClosed()) {
143
0
                                SkPathOpsDebug::ShowActiveSpans(contourList);
144
0
                            }
145
65.4k
                        } else if (lastSimple) {
146
10.4k
                            if (!current->addCurveTo(start, end, writer)) {
147
1.20k
                                return false;
148
1.20k
                            }
149
64.2k
                        }
150
64.2k
                        break;
151
64.2k
                    }
152
        #if DEBUG_FLOW
153
                    SkDebugf("%s current id=%d from=(%1.9g,%1.9g) to=(%1.9g,%1.9g)\n", __FUNCTION__,
154
                            current->debugID(), start->pt().fX, start->pt().fY,
155
                            end->pt().fX, end->pt().fY);
156
        #endif
157
5.64M
                    if (!current->addCurveTo(start, end, writer)) {
158
13.0k
                        return false;
159
13.0k
                    }
160
5.62M
                    current = next;
161
5.62M
                    start = nextStart;
162
5.62M
                    end = nextEnd;
163
5.62M
                } while (!writer->isClosed() && (!unsortable || !start->starter(end)->done()));
164
717k
                if (current->activeWinding(start, end) && !writer->isClosed()) {
165
229k
                    SkOpSpan* spanStart = start->starter(end);
166
229k
                    if (!spanStart->done()) {
167
197
                        if (!current->addCurveTo(start, end, writer)) {
168
121
                            return false;
169
121
                        }
170
76
                        current->markDone(spanStart);
171
76
                    }
172
229k
                }
173
717k
                writer->finishContour();
174
1.78M
            } else {
175
1.78M
                SkOpSpanBase* last;
176
1.78M
                if (!current->markAndChaseDone(start, end, &last)) {
177
10
                    return false;
178
10
                }
179
1.78M
                if (last && !last->chased()) {
180
1.48M
                    last->setChased(true);
181
1.48M
                    SkASSERT(!SkPathOpsDebug::ChaseContains(chase, last));
182
1.48M
                    *chase.append() = last;
183
#if DEBUG_WINDING
184
                    SkDebugf("%s chase.append id=%d", __FUNCTION__, last->segment()->debugID());
185
                    if (!last->final()) {
186
                         SkDebugf(" windSum=%d", last->upCast()->windSum());
187
                    }
188
                    SkDebugf("\n");
189
#endif
190
1.48M
                }
191
1.78M
            }
192
2.49M
            if (!findChaseOp(chase, &start, &end, &current)) {
193
1.05k
                return false;
194
1.05k
            }
195
2.49M
            SkPathOpsDebug::ShowActiveSpans(contourList);
196
2.49M
            if (!current) {
197
398k
                break;
198
398k
            }
199
2.10M
        } while (true);
200
398k
    } while (true);
201
157k
    return true;
202
172k
}
203
204
// diagram of why this simplifcation is possible is here:
205
// https://skia.org/dev/present/pathops link at bottom of the page
206
// https://drive.google.com/file/d/0BwoLUwz9PYkHLWpsaXd0UDdaN00/view?usp=sharing
207
static const SkPathOp gOpInverse[kReverseDifference_SkPathOp + 1][2][2] = {
208
//                  inside minuend                               outside minuend
209
//     inside subtrahend     outside subtrahend      inside subtrahend     outside subtrahend
210
{{ kDifference_SkPathOp,   kIntersect_SkPathOp }, { kUnion_SkPathOp, kReverseDifference_SkPathOp }},
211
{{ kIntersect_SkPathOp,   kDifference_SkPathOp }, { kReverseDifference_SkPathOp, kUnion_SkPathOp }},
212
{{ kUnion_SkPathOp, kReverseDifference_SkPathOp }, { kDifference_SkPathOp,   kIntersect_SkPathOp }},
213
{{ kXOR_SkPathOp,                 kXOR_SkPathOp }, { kXOR_SkPathOp,                kXOR_SkPathOp }},
214
{{ kReverseDifference_SkPathOp, kUnion_SkPathOp }, { kIntersect_SkPathOp,   kDifference_SkPathOp }},
215
};
216
217
static const bool gOutInverse[kReverseDifference_SkPathOp + 1][2][2] = {
218
    {{ false, false }, { true, false }},  // diff
219
    {{ false, false }, { false, true }},  // sect
220
    {{ false, true }, { true, true }},    // union
221
    {{ false, true }, { true, false }},   // xor
222
    {{ false, true }, { false, false }},  // rev diff
223
};
224
225
#if DEBUG_T_SECT_LOOP_COUNT
226
227
#include "include/private/SkMutex.h"
228
229
SkOpGlobalState debugWorstState(nullptr, nullptr  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr));
230
231
void ReportPathOpsDebugging() {
232
    debugWorstState.debugLoopReport();
233
}
234
235
extern void (*gVerboseFinalize)();
236
237
#endif
238
239
bool OpDebug(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result
240
382k
        SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
241
#if DEBUG_DUMP_VERIFY
242
#ifndef SK_DEBUG
243
    const char* testName = "release";
244
#endif
245
    if (SkPathOpsDebug::gDumpOp) {
246
        SkPathOpsDebug::DumpOp(one, two, op, testName);
247
    }
248
#endif
249
382k
    op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
250
382k
    bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
251
6.59k
    SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd :
252
376k
            SkPathFillType::kEvenOdd;
253
382k
    SkRect rect1, rect2;
254
382k
    if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
255
288
        result->reset();
256
288
        result->setFillType(fillType);
257
288
        if (rect1.intersect(rect2)) {
258
288
            result->addRect(rect1);
259
288
        }
260
288
        return true;
261
288
    }
262
382k
    if (one.isEmpty() || two.isEmpty()) {
263
185k
        SkPath work;
264
185k
        switch (op) {
265
1.36k
            case kIntersect_SkPathOp:
266
1.36k
                break;
267
169k
            case kUnion_SkPathOp:
268
174k
            case kXOR_SkPathOp:
269
130k
                work = one.isEmpty() ? two : one;
270
174k
                break;
271
9.30k
            case kDifference_SkPathOp:
272
9.30k
                if (!one.isEmpty()) {
273
8.50k
                    work = one;
274
8.50k
                }
275
9.30k
                break;
276
609
            case kReverseDifference_SkPathOp:
277
609
                if (!two.isEmpty()) {
278
392
                    work = two;
279
392
                }
280
609
                break;
281
0
            default:
282
0
                SkASSERT(0);  // unhandled case
283
185k
        }
284
185k
        if (inverseFill != work.isInverseFillType()) {
285
7.12k
            work.toggleInverseFillType();
286
7.12k
        }
287
185k
        return Simplify(work, result);
288
196k
    }
289
196k
    SkSTArenaAlloc<4096> allocator;  // FIXME: add a constant expression here, tune
290
196k
    SkOpContour contour;
291
196k
    SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
292
196k
    SkOpGlobalState globalState(contourList, &allocator
293
0
            SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
294
196k
    SkOpCoincidence coincidence(&globalState);
295
196k
    const SkPath* minuend = &one;
296
196k
    const SkPath* subtrahend = &two;
297
196k
    if (op == kReverseDifference_SkPathOp) {
298
197
        using std::swap;
299
197
        swap(minuend, subtrahend);
300
197
        op = kDifference_SkPathOp;
301
197
    }
302
#if DEBUG_SORT
303
    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
304
#endif
305
    // turn path into list of segments
306
196k
    SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
307
196k
    if (builder.unparseable()) {
308
195
        return false;
309
195
    }
310
196k
    const int xorMask = builder.xorMask();
311
196k
    builder.addOperand(*subtrahend);
312
196k
    if (!builder.finish()) {
313
1.70k
        return false;
314
1.70k
    }
315
#if DEBUG_DUMP_SEGMENTS
316
    contourList->dumpSegments("seg", op);
317
#endif
318
319
194k
    const int xorOpMask = builder.xorMask();
320
194k
    if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
321
3.28k
            xorOpMask == kEvenOdd_PathOpsMask)) {
322
3.28k
        result->reset();
323
3.28k
        result->setFillType(fillType);
324
3.28k
        return true;
325
3.28k
    }
326
    // find all intersections between segments
327
191k
    SkOpContour* current = contourList;
328
843k
    do {
329
843k
        SkOpContour* next = current;
330
3.77M
        while (AddIntersectTs(current, next, &coincidence)
331
3.49M
                && (next = next->next()))
332
2.92M
            ;
333
843k
    } while ((current = current->next()));
334
#if DEBUG_VALIDATE
335
    globalState.setPhase(SkOpPhase::kWalking);
336
#endif
337
191k
    bool success = HandleCoincidence(contourList, &coincidence);
338
#if DEBUG_COIN
339
    globalState.debugAddToGlobalCoinDicts();
340
#endif
341
191k
    if (!success) {
342
18.5k
        return false;
343
18.5k
    }
344
#if DEBUG_ALIGNMENT
345
    contourList->dumpSegments("aligned");
346
#endif
347
    // construct closed contours
348
172k
    SkPath original = *result;
349
172k
    result->reset();
350
172k
    result->setFillType(fillType);
351
172k
    SkPathWriter wrapper(*result);
352
172k
    if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
353
15.4k
        *result = original;
354
15.4k
        return false;
355
15.4k
    }
356
157k
    wrapper.assemble();  // if some edges could not be resolved, assemble remaining
357
#if DEBUG_T_SECT_LOOP_COUNT
358
    static SkMutex& debugWorstLoop = *(new SkMutex);
359
    {
360
        SkAutoMutexExclusive autoM(debugWorstLoop);
361
        if (!gVerboseFinalize) {
362
            gVerboseFinalize = &ReportPathOpsDebugging;
363
        }
364
        debugWorstState.debugDoYourWorst(&globalState);
365
    }
366
#endif
367
157k
    return true;
368
157k
}
OpDebug(SkPath const&, SkPath const&, SkPathOp, SkPath*)
Line
Count
Source
240
382k
        SkDEBUGPARAMS(bool skipAssert) SkDEBUGPARAMS(const char* testName)) {
241
#if DEBUG_DUMP_VERIFY
242
#ifndef SK_DEBUG
243
    const char* testName = "release";
244
#endif
245
    if (SkPathOpsDebug::gDumpOp) {
246
        SkPathOpsDebug::DumpOp(one, two, op, testName);
247
    }
248
#endif
249
382k
    op = gOpInverse[op][one.isInverseFillType()][two.isInverseFillType()];
250
382k
    bool inverseFill = gOutInverse[op][one.isInverseFillType()][two.isInverseFillType()];
251
6.59k
    SkPathFillType fillType = inverseFill ? SkPathFillType::kInverseEvenOdd :
252
376k
            SkPathFillType::kEvenOdd;
253
382k
    SkRect rect1, rect2;
254
382k
    if (kIntersect_SkPathOp == op && one.isRect(&rect1) && two.isRect(&rect2)) {
255
288
        result->reset();
256
288
        result->setFillType(fillType);
257
288
        if (rect1.intersect(rect2)) {
258
288
            result->addRect(rect1);
259
288
        }
260
288
        return true;
261
288
    }
262
382k
    if (one.isEmpty() || two.isEmpty()) {
263
185k
        SkPath work;
264
185k
        switch (op) {
265
1.36k
            case kIntersect_SkPathOp:
266
1.36k
                break;
267
169k
            case kUnion_SkPathOp:
268
174k
            case kXOR_SkPathOp:
269
130k
                work = one.isEmpty() ? two : one;
270
174k
                break;
271
9.30k
            case kDifference_SkPathOp:
272
9.30k
                if (!one.isEmpty()) {
273
8.50k
                    work = one;
274
8.50k
                }
275
9.30k
                break;
276
609
            case kReverseDifference_SkPathOp:
277
609
                if (!two.isEmpty()) {
278
392
                    work = two;
279
392
                }
280
609
                break;
281
0
            default:
282
0
                SkASSERT(0);  // unhandled case
283
185k
        }
284
185k
        if (inverseFill != work.isInverseFillType()) {
285
7.12k
            work.toggleInverseFillType();
286
7.12k
        }
287
185k
        return Simplify(work, result);
288
196k
    }
289
196k
    SkSTArenaAlloc<4096> allocator;  // FIXME: add a constant expression here, tune
290
196k
    SkOpContour contour;
291
196k
    SkOpContourHead* contourList = static_cast<SkOpContourHead*>(&contour);
292
196k
    SkOpGlobalState globalState(contourList, &allocator
293
196k
            SkDEBUGPARAMS(skipAssert) SkDEBUGPARAMS(testName));
294
196k
    SkOpCoincidence coincidence(&globalState);
295
196k
    const SkPath* minuend = &one;
296
196k
    const SkPath* subtrahend = &two;
297
196k
    if (op == kReverseDifference_SkPathOp) {
298
197
        using std::swap;
299
197
        swap(minuend, subtrahend);
300
197
        op = kDifference_SkPathOp;
301
197
    }
302
#if DEBUG_SORT
303
    SkPathOpsDebug::gSortCount = SkPathOpsDebug::gSortCountDefault;
304
#endif
305
    // turn path into list of segments
306
196k
    SkOpEdgeBuilder builder(*minuend, contourList, &globalState);
307
196k
    if (builder.unparseable()) {
308
195
        return false;
309
195
    }
310
196k
    const int xorMask = builder.xorMask();
311
196k
    builder.addOperand(*subtrahend);
312
196k
    if (!builder.finish()) {
313
1.70k
        return false;
314
1.70k
    }
315
#if DEBUG_DUMP_SEGMENTS
316
    contourList->dumpSegments("seg", op);
317
#endif
318
319
194k
    const int xorOpMask = builder.xorMask();
320
194k
    if (!SortContourList(&contourList, xorMask == kEvenOdd_PathOpsMask,
321
3.28k
            xorOpMask == kEvenOdd_PathOpsMask)) {
322
3.28k
        result->reset();
323
3.28k
        result->setFillType(fillType);
324
3.28k
        return true;
325
3.28k
    }
326
    // find all intersections between segments
327
191k
    SkOpContour* current = contourList;
328
843k
    do {
329
843k
        SkOpContour* next = current;
330
3.77M
        while (AddIntersectTs(current, next, &coincidence)
331
3.49M
                && (next = next->next()))
332
2.92M
            ;
333
843k
    } while ((current = current->next()));
334
#if DEBUG_VALIDATE
335
    globalState.setPhase(SkOpPhase::kWalking);
336
#endif
337
191k
    bool success = HandleCoincidence(contourList, &coincidence);
338
#if DEBUG_COIN
339
    globalState.debugAddToGlobalCoinDicts();
340
#endif
341
191k
    if (!success) {
342
18.5k
        return false;
343
18.5k
    }
344
#if DEBUG_ALIGNMENT
345
    contourList->dumpSegments("aligned");
346
#endif
347
    // construct closed contours
348
172k
    SkPath original = *result;
349
172k
    result->reset();
350
172k
    result->setFillType(fillType);
351
172k
    SkPathWriter wrapper(*result);
352
172k
    if (!bridgeOp(contourList, op, xorMask, xorOpMask, &wrapper)) {
353
15.4k
        *result = original;
354
15.4k
        return false;
355
15.4k
    }
356
157k
    wrapper.assemble();  // if some edges could not be resolved, assemble remaining
357
#if DEBUG_T_SECT_LOOP_COUNT
358
    static SkMutex& debugWorstLoop = *(new SkMutex);
359
    {
360
        SkAutoMutexExclusive autoM(debugWorstLoop);
361
        if (!gVerboseFinalize) {
362
            gVerboseFinalize = &ReportPathOpsDebugging;
363
        }
364
        debugWorstState.debugDoYourWorst(&globalState);
365
    }
366
#endif
367
157k
    return true;
368
157k
}
Unexecuted instantiation: OpDebug(SkPath const&, SkPath const&, SkPathOp, SkPath*, bool, char const*)
369
370
765k
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
371
#if DEBUG_DUMP_VERIFY
372
    if (SkPathOpsDebug::gVerifyOp) {
373
        if (!OpDebug(one, two, op, result  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
374
            SkPathOpsDebug::ReportOpFail(one, two, op);
375
            return false;
376
        }
377
        SkPathOpsDebug::VerifyOp(one, two, op, *result);
378
        return true;
379
    }
380
#endif
381
382k
    return OpDebug(one, two, op, result  SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
382
765k
}
Op(SkPath const&, SkPath const&, SkPathOp, SkPath*)
Line
Count
Source
370
382k
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
371
#if DEBUG_DUMP_VERIFY
372
    if (SkPathOpsDebug::gVerifyOp) {
373
        if (!OpDebug(one, two, op, result  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
374
            SkPathOpsDebug::ReportOpFail(one, two, op);
375
            return false;
376
        }
377
        SkPathOpsDebug::VerifyOp(one, two, op, *result);
378
        return true;
379
    }
380
#endif
381
382k
    return OpDebug(one, two, op, result  SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
382
382k
}
Op(SkPath const&, SkPath const&, SkPathOp, SkPath*)
Line
Count
Source
370
382k
bool Op(const SkPath& one, const SkPath& two, SkPathOp op, SkPath* result) {
371
#if DEBUG_DUMP_VERIFY
372
    if (SkPathOpsDebug::gVerifyOp) {
373
        if (!OpDebug(one, two, op, result  SkDEBUGPARAMS(false) SkDEBUGPARAMS(nullptr))) {
374
            SkPathOpsDebug::ReportOpFail(one, two, op);
375
            return false;
376
        }
377
        SkPathOpsDebug::VerifyOp(one, two, op, *result);
378
        return true;
379
    }
380
#endif
381
382k
    return OpDebug(one, two, op, result  SkDEBUGPARAMS(true) SkDEBUGPARAMS(nullptr));
382
382k
}