Coverage Report

Created: 2024-05-20 07:14

/src/skia/src/core/SkClipStack.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2011 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
8
#include "src/core/SkClipStack.h"
9
10
#include "include/core/SkBlendMode.h"
11
#include "include/core/SkPath.h"
12
#include "include/core/SkPathTypes.h"
13
#include "include/core/SkScalar.h"
14
#include "include/private/base/SkDebug.h"
15
#include "src/core/SkRectPriv.h"
16
#include "src/shaders/SkShaderBase.h"
17
18
#include <array>
19
#include <atomic>
20
#include <new>
21
22
6.48k
SkClipStack::Element::Element(const Element& that) {
23
6.48k
    switch (that.getDeviceSpaceType()) {
24
0
        case DeviceSpaceType::kEmpty:
25
0
            fDeviceSpaceRRect.setEmpty();
26
0
            fDeviceSpacePath.reset();
27
0
            fShader.reset();
28
0
            break;
29
4.46k
        case DeviceSpaceType::kRect:  // Rect uses rrect
30
4.69k
        case DeviceSpaceType::kRRect:
31
4.69k
            fDeviceSpacePath.reset();
32
4.69k
            fShader.reset();
33
4.69k
            fDeviceSpaceRRect = that.fDeviceSpaceRRect;
34
4.69k
            break;
35
1.79k
        case DeviceSpaceType::kPath:
36
1.79k
            fShader.reset();
37
1.79k
            fDeviceSpacePath.set(that.getDeviceSpacePath());
38
1.79k
            break;
39
0
        case DeviceSpaceType::kShader:
40
0
            fDeviceSpacePath.reset();
41
0
            fShader = that.fShader;
42
0
            break;
43
6.48k
    }
44
45
6.48k
    fSaveCount = that.fSaveCount;
46
6.48k
    fOp = that.fOp;
47
6.48k
    fDeviceSpaceType = that.fDeviceSpaceType;
48
6.48k
    fDoAA = that.fDoAA;
49
6.48k
    fIsReplace = that.fIsReplace;
50
6.48k
    fFiniteBoundType = that.fFiniteBoundType;
51
6.48k
    fFiniteBound = that.fFiniteBound;
52
6.48k
    fIsIntersectionOfRects = that.fIsIntersectionOfRects;
53
6.48k
    fGenID = that.fGenID;
54
6.48k
}
55
56
13.5k
SkClipStack::Element::~Element() = default;
57
58
0
bool SkClipStack::Element::operator== (const Element& element) const {
59
0
    if (this == &element) {
60
0
        return true;
61
0
    }
62
0
    if (fOp != element.fOp || fDeviceSpaceType != element.fDeviceSpaceType ||
63
0
        fDoAA != element.fDoAA || fIsReplace != element.fIsReplace ||
64
0
        fSaveCount != element.fSaveCount) {
65
0
        return false;
66
0
    }
67
0
    switch (fDeviceSpaceType) {
68
0
        case DeviceSpaceType::kShader:
69
0
            return this->getShader() == element.getShader();
70
0
        case DeviceSpaceType::kPath:
71
0
            return this->getDeviceSpacePath() == element.getDeviceSpacePath();
72
0
        case DeviceSpaceType::kRRect:
73
0
            return fDeviceSpaceRRect == element.fDeviceSpaceRRect;
74
0
        case DeviceSpaceType::kRect:
75
0
            return this->getDeviceSpaceRect() == element.getDeviceSpaceRect();
76
0
        case DeviceSpaceType::kEmpty:
77
0
            return true;
78
0
        default:
79
0
            SkDEBUGFAIL("Unexpected type.");
80
0
            return false;
81
0
    }
82
0
}
83
84
512
const SkRect& SkClipStack::Element::getBounds() const {
85
512
    static const SkRect kEmpty = {0, 0, 0, 0};
86
512
    static const SkRect kInfinite = SkRectPriv::MakeLargeS32();
87
512
    switch (fDeviceSpaceType) {
88
75
        case DeviceSpaceType::kRect:  // fallthrough
89
475
        case DeviceSpaceType::kRRect:
90
475
            return fDeviceSpaceRRect.getBounds();
91
37
        case DeviceSpaceType::kPath:
92
37
            return fDeviceSpacePath->getBounds();
93
0
        case DeviceSpaceType::kShader:
94
            // Shaders have infinite bounds since any pixel could have clipped or full coverage
95
            // (which is different from wide-open, where every pixel has 1.0 coverage, or empty
96
            //  where every pixel has 0.0 coverage).
97
0
            return kInfinite;
98
0
        case DeviceSpaceType::kEmpty:
99
0
            return kEmpty;
100
0
        default:
101
0
            SkDEBUGFAIL("Unexpected type.");
102
0
            return kEmpty;
103
512
    }
104
512
}
105
106
0
bool SkClipStack::Element::contains(const SkRect& rect) const {
107
0
    switch (fDeviceSpaceType) {
108
0
        case DeviceSpaceType::kRect:
109
0
            return this->getDeviceSpaceRect().contains(rect);
110
0
        case DeviceSpaceType::kRRect:
111
0
            return fDeviceSpaceRRect.contains(rect);
112
0
        case DeviceSpaceType::kPath:
113
0
            return fDeviceSpacePath->conservativelyContainsRect(rect);
114
0
        case DeviceSpaceType::kEmpty:
115
0
        case DeviceSpaceType::kShader:
116
0
            return false;
117
0
        default:
118
0
            SkDEBUGFAIL("Unexpected type.");
119
0
            return false;
120
0
    }
121
0
}
122
123
0
bool SkClipStack::Element::contains(const SkRRect& rrect) const {
124
0
    switch (fDeviceSpaceType) {
125
0
        case DeviceSpaceType::kRect:
126
0
            return this->getDeviceSpaceRect().contains(rrect.getBounds());
127
0
        case DeviceSpaceType::kRRect:
128
            // We don't currently have a generalized rrect-rrect containment.
129
0
            return fDeviceSpaceRRect.contains(rrect.getBounds()) || rrect == fDeviceSpaceRRect;
130
0
        case DeviceSpaceType::kPath:
131
0
            return fDeviceSpacePath->conservativelyContainsRect(rrect.getBounds());
132
0
        case DeviceSpaceType::kEmpty:
133
0
        case DeviceSpaceType::kShader:
134
0
            return false;
135
0
        default:
136
0
            SkDEBUGFAIL("Unexpected type.");
137
0
            return false;
138
0
    }
139
0
}
140
141
0
void SkClipStack::Element::invertShapeFillType() {
142
0
    switch (fDeviceSpaceType) {
143
0
        case DeviceSpaceType::kRect:
144
0
            fDeviceSpacePath.init();
145
0
            fDeviceSpacePath->addRect(this->getDeviceSpaceRect());
146
0
            fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
147
0
            fDeviceSpaceType = DeviceSpaceType::kPath;
148
0
            break;
149
0
        case DeviceSpaceType::kRRect:
150
0
            fDeviceSpacePath.init();
151
0
            fDeviceSpacePath->addRRect(fDeviceSpaceRRect);
152
0
            fDeviceSpacePath->setFillType(SkPathFillType::kInverseEvenOdd);
153
0
            fDeviceSpaceType = DeviceSpaceType::kPath;
154
0
            break;
155
0
        case DeviceSpaceType::kPath:
156
0
            fDeviceSpacePath->toggleInverseFillType();
157
0
            break;
158
0
        case DeviceSpaceType::kShader:
159
0
            fShader = as_SB(fShader)->makeInvertAlpha();
160
0
            break;
161
0
        case DeviceSpaceType::kEmpty:
162
            // Should this set to an empty, inverse filled path?
163
0
            break;
164
0
    }
165
0
}
166
167
7.09k
void SkClipStack::Element::initCommon(int saveCount, SkClipOp op, bool doAA) {
168
7.09k
    fSaveCount = saveCount;
169
7.09k
    fOp = op;
170
7.09k
    fDoAA = doAA;
171
7.09k
    fIsReplace = false;
172
    // A default of inside-out and empty bounds means the bounds are effectively void as it
173
    // indicates that nothing is known to be outside the clip.
174
7.09k
    fFiniteBoundType = kInsideOut_BoundsType;
175
7.09k
    fFiniteBound.setEmpty();
176
7.09k
    fIsIntersectionOfRects = false;
177
7.09k
    fGenID = kInvalidGenID;
178
7.09k
}
179
180
void SkClipStack::Element::initRect(int saveCount, const SkRect& rect, const SkMatrix& m,
181
5.47k
                                    SkClipOp op, bool doAA) {
182
5.47k
    if (m.rectStaysRect()) {
183
4.69k
        SkRect devRect;
184
4.69k
        m.mapRect(&devRect, rect);
185
4.69k
        fDeviceSpaceRRect.setRect(devRect);
186
4.69k
        fDeviceSpaceType = DeviceSpaceType::kRect;
187
4.69k
        this->initCommon(saveCount, op, doAA);
188
4.69k
        return;
189
4.69k
    }
190
780
    SkPath path;
191
780
    path.addRect(rect);
192
780
    path.setIsVolatile(true);
193
780
    this->initAsPath(saveCount, path, m, op, doAA);
194
780
}
195
196
void SkClipStack::Element::initRRect(int saveCount, const SkRRect& rrect, const SkMatrix& m,
197
530
                                     SkClipOp op, bool doAA) {
198
530
    if (rrect.transform(m, &fDeviceSpaceRRect)) {
199
376
        SkRRect::Type type = fDeviceSpaceRRect.getType();
200
376
        if (SkRRect::kRect_Type == type || SkRRect::kEmpty_Type == type) {
201
99
            fDeviceSpaceType = DeviceSpaceType::kRect;
202
277
        } else {
203
277
            fDeviceSpaceType = DeviceSpaceType::kRRect;
204
277
        }
205
376
        this->initCommon(saveCount, op, doAA);
206
376
        return;
207
376
    }
208
154
    SkPath path;
209
154
    path.addRRect(rrect);
210
154
    path.setIsVolatile(true);
211
154
    this->initAsPath(saveCount, path, m, op, doAA);
212
154
}
213
214
void SkClipStack::Element::initPath(int saveCount, const SkPath& path, const SkMatrix& m,
215
1.18k
                                    SkClipOp op, bool doAA) {
216
1.18k
    if (!path.isInverseFillType()) {
217
536
        SkRect r;
218
536
        if (path.isRect(&r)) {
219
90
            this->initRect(saveCount, r, m, op, doAA);
220
90
            return;
221
90
        }
222
446
        SkRect ovalRect;
223
446
        if (path.isOval(&ovalRect)) {
224
0
            SkRRect rrect;
225
0
            rrect.setOval(ovalRect);
226
0
            this->initRRect(saveCount, rrect, m, op, doAA);
227
0
            return;
228
0
        }
229
446
    }
230
1.09k
    this->initAsPath(saveCount, path, m, op, doAA);
231
1.09k
}
232
233
void SkClipStack::Element::initAsPath(int saveCount, const SkPath& path, const SkMatrix& m,
234
2.02k
                                      SkClipOp op, bool doAA) {
235
2.02k
    path.transform(m, fDeviceSpacePath.init());
236
2.02k
    fDeviceSpacePath->setIsVolatile(true);
237
2.02k
    fDeviceSpaceType = DeviceSpaceType::kPath;
238
2.02k
    this->initCommon(saveCount, op, doAA);
239
2.02k
}
240
241
0
void SkClipStack::Element::initShader(int saveCount, sk_sp<SkShader> shader) {
242
0
    SkASSERT(shader);
243
0
    fDeviceSpaceType = DeviceSpaceType::kShader;
244
0
    fShader = std::move(shader);
245
0
    this->initCommon(saveCount, SkClipOp::kIntersect, false);
246
0
}
247
248
0
void SkClipStack::Element::initReplaceRect(int saveCount, const SkRect& rect, bool doAA) {
249
0
    fDeviceSpaceRRect.setRect(rect);
250
0
    fDeviceSpaceType = DeviceSpaceType::kRect;
251
0
    this->initCommon(saveCount, SkClipOp::kIntersect, doAA);
252
0
    fIsReplace = true;
253
0
}
254
255
0
void SkClipStack::Element::asDeviceSpacePath(SkPath* path) const {
256
0
    switch (fDeviceSpaceType) {
257
0
        case DeviceSpaceType::kEmpty:
258
0
            path->reset();
259
0
            break;
260
0
        case DeviceSpaceType::kRect:
261
0
            path->reset();
262
0
            path->addRect(this->getDeviceSpaceRect());
263
0
            break;
264
0
        case DeviceSpaceType::kRRect:
265
0
            path->reset();
266
0
            path->addRRect(fDeviceSpaceRRect);
267
0
            break;
268
0
        case DeviceSpaceType::kPath:
269
0
            *path = *fDeviceSpacePath;
270
0
            break;
271
0
        case DeviceSpaceType::kShader:
272
0
            path->reset();
273
0
            path->addRect(SkRectPriv::MakeLargeS32());
274
0
            break;
275
0
    }
276
0
    path->setIsVolatile(true);
277
0
}
278
279
305
void SkClipStack::Element::setEmpty() {
280
305
    fDeviceSpaceType = DeviceSpaceType::kEmpty;
281
305
    fFiniteBound.setEmpty();
282
305
    fFiniteBoundType = kNormal_BoundsType;
283
305
    fIsIntersectionOfRects = false;
284
305
    fDeviceSpaceRRect.setEmpty();
285
305
    fDeviceSpacePath.reset();
286
305
    fShader.reset();
287
305
    fGenID = kEmptyGenID;
288
305
    SkDEBUGCODE(this->checkEmpty();)
289
305
}
290
291
0
void SkClipStack::Element::checkEmpty() const {
292
0
    SkASSERT(fFiniteBound.isEmpty());
293
0
    SkASSERT(kNormal_BoundsType == fFiniteBoundType);
294
0
    SkASSERT(!fIsIntersectionOfRects);
295
0
    SkASSERT(kEmptyGenID == fGenID);
296
0
    SkASSERT(fDeviceSpaceRRect.isEmpty());
297
0
    SkASSERT(!fDeviceSpacePath.isValid());
298
0
    SkASSERT(!fShader);
299
0
}
300
301
2.09k
bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkClipOp op) const {
302
2.09k
    if (DeviceSpaceType::kEmpty == fDeviceSpaceType &&
303
2.09k
        (SkClipOp::kDifference == op || SkClipOp::kIntersect == op)) {
304
584
        return true;
305
584
    }
306
    // Only clips within the same save/restore frame (as captured by
307
    // the save count) can be merged
308
1.50k
    return  fSaveCount == saveCount &&
309
1.50k
            SkClipOp::kIntersect == op &&
310
1.50k
            (SkClipOp::kIntersect == fOp || this->isReplaceOp());
311
2.09k
}
312
313
140
bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
314
140
    SkASSERT(DeviceSpaceType::kRect == fDeviceSpaceType);
315
316
140
    if (fDoAA == newAA) {
317
        // if the AA setting is the same there is no issue
318
122
        return true;
319
122
    }
320
321
18
    if (!SkRect::Intersects(this->getDeviceSpaceRect(), newR)) {
322
        // The calling code will correctly set the result to the empty clip
323
18
        return true;
324
18
    }
325
326
0
    if (this->getDeviceSpaceRect().contains(newR)) {
327
        // if the new rect carves out a portion of the old one there is no
328
        // issue
329
0
        return true;
330
0
    }
331
332
    // So either the two overlap in some complex manner or newR contains oldR.
333
    // In the first, case the edges will require different AA. In the second,
334
    // the AA setting that would be carried forward is incorrect (e.g., oldR
335
    // is AA while newR is BW but since newR contains oldR, oldR will be
336
    // drawn BW) since the new AA setting will predominate.
337
0
    return false;
338
0
}
339
340
// a mirror of combineBoundsRevDiff
341
996
void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
342
996
    switch (combination) {
343
255
        case kInvPrev_InvCur_FillCombo:
344
            // In this case the only pixels that can remain set
345
            // are inside the current clip rect since the extensions
346
            // to infinity of both clips cancel out and whatever
347
            // is outside of the current clip is removed
348
255
            fFiniteBoundType = kNormal_BoundsType;
349
255
            break;
350
575
        case kInvPrev_Cur_FillCombo:
351
            // In this case the current op is finite so the only pixels
352
            // that aren't set are whatever isn't set in the previous
353
            // clip and whatever this clip carves out
354
575
            fFiniteBound.join(prevFinite);
355
575
            fFiniteBoundType = kInsideOut_BoundsType;
356
575
            break;
357
0
        case kPrev_InvCur_FillCombo:
358
            // In this case everything outside of this clip's bound
359
            // is erased, so the only pixels that can remain set
360
            // occur w/in the intersection of the two finite bounds
361
0
            if (!fFiniteBound.intersect(prevFinite)) {
362
0
                fFiniteBound.setEmpty();
363
0
                fGenID = kEmptyGenID;
364
0
            }
365
0
            fFiniteBoundType = kNormal_BoundsType;
366
0
            break;
367
166
        case kPrev_Cur_FillCombo:
368
            // The most conservative result bound is that of the
369
            // prior clip. This could be wildly incorrect if the
370
            // second clip either exactly matches the first clip
371
            // (which should yield the empty set) or reduces the
372
            // size of the prior bound (e.g., if the second clip
373
            // exactly matched the bottom half of the prior clip).
374
            // We ignore these two possibilities.
375
166
            fFiniteBound = prevFinite;
376
166
            break;
377
0
        default:
378
0
            SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
379
0
            break;
380
996
    }
381
996
}
382
383
// a mirror of combineBoundsUnion
384
5.49k
void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
385
386
5.49k
    switch (combination) {
387
382
        case kInvPrev_InvCur_FillCombo:
388
            // The only pixels that aren't writable in this case
389
            // occur in the union of the two finite bounds
390
382
            fFiniteBound.join(prevFinite);
391
382
            fFiniteBoundType = kInsideOut_BoundsType;
392
382
            break;
393
4.57k
        case kInvPrev_Cur_FillCombo:
394
            // In this case the only pixels that will remain writeable
395
            // are within the current clip
396
4.57k
            break;
397
8
        case kPrev_InvCur_FillCombo:
398
            // In this case the only pixels that will remain writeable
399
            // are with the previous clip
400
8
            fFiniteBound = prevFinite;
401
8
            fFiniteBoundType = kNormal_BoundsType;
402
8
            break;
403
530
        case kPrev_Cur_FillCombo:
404
530
            if (!fFiniteBound.intersect(prevFinite)) {
405
288
                this->setEmpty();
406
288
            }
407
530
            break;
408
0
        default:
409
0
            SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
410
0
            break;
411
5.49k
    }
412
5.49k
}
413
414
6.49k
void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
415
    // We set this first here but we may overwrite it later if we determine that the clip is
416
    // either wide-open or empty.
417
6.49k
    fGenID = GetNextGenID();
418
419
    // First, optimistically update the current Element's bound information
420
    // with the current clip's bound
421
6.49k
    fIsIntersectionOfRects = false;
422
6.49k
    switch (fDeviceSpaceType) {
423
4.47k
        case DeviceSpaceType::kRect:
424
4.47k
            fFiniteBound = this->getDeviceSpaceRect();
425
4.47k
            fFiniteBoundType = kNormal_BoundsType;
426
427
4.47k
            if (this->isReplaceOp() ||
428
4.47k
                (SkClipOp::kIntersect == fOp && nullptr == prior) ||
429
4.47k
                (SkClipOp::kIntersect == fOp && prior->fIsIntersectionOfRects &&
430
3.80k
                 prior->rectRectIntersectAllowed(this->getDeviceSpaceRect(), fDoAA))) {
431
3.80k
                fIsIntersectionOfRects = true;
432
3.80k
            }
433
4.47k
            break;
434
229
        case DeviceSpaceType::kRRect:
435
229
            fFiniteBound = fDeviceSpaceRRect.getBounds();
436
229
            fFiniteBoundType = kNormal_BoundsType;
437
229
            break;
438
1.79k
        case DeviceSpaceType::kPath:
439
1.79k
            fFiniteBound = fDeviceSpacePath->getBounds();
440
441
1.79k
            if (fDeviceSpacePath->isInverseFillType()) {
442
645
                fFiniteBoundType = kInsideOut_BoundsType;
443
1.14k
            } else {
444
1.14k
                fFiniteBoundType = kNormal_BoundsType;
445
1.14k
            }
446
1.79k
            break;
447
0
        case DeviceSpaceType::kShader:
448
            // A shader is infinite. We don't act as wide-open here (which is an empty bounds with
449
            // the inside out type). This is because when the bounds is empty and inside-out, we
450
            // know there's full coverage everywhere. With a shader, there's *unknown* coverage
451
            // everywhere.
452
0
            fFiniteBound = SkRectPriv::MakeLargeS32();
453
0
            fFiniteBoundType = kNormal_BoundsType;
454
0
            break;
455
0
        case DeviceSpaceType::kEmpty:
456
0
            SkDEBUGFAIL("We shouldn't get here with an empty element.");
457
0
            break;
458
6.49k
    }
459
460
    // Now determine the previous Element's bound information taking into
461
    // account that there may be no previous clip
462
6.49k
    SkRect prevFinite;
463
6.49k
    SkClipStack::BoundsType prevType;
464
465
6.49k
    if (nullptr == prior) {
466
        // no prior clip means the entire plane is writable
467
5.00k
        prevFinite.setEmpty();   // there are no pixels that cannot be drawn to
468
5.00k
        prevType = kInsideOut_BoundsType;
469
5.00k
    } else {
470
1.48k
        prevFinite = prior->fFiniteBound;
471
1.48k
        prevType = prior->fFiniteBoundType;
472
1.48k
    }
473
474
6.49k
    FillCombo combination = kPrev_Cur_FillCombo;
475
6.49k
    if (kInsideOut_BoundsType == fFiniteBoundType) {
476
645
        combination = (FillCombo) (combination | 0x01);
477
645
    }
478
6.49k
    if (kInsideOut_BoundsType == prevType) {
479
5.78k
        combination = (FillCombo) (combination | 0x02);
480
5.78k
    }
481
482
6.49k
    SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
483
6.49k
                kInvPrev_Cur_FillCombo == combination ||
484
6.49k
                kPrev_InvCur_FillCombo == combination ||
485
6.49k
                kPrev_Cur_FillCombo == combination);
486
487
    // Now integrate with clip with the prior clips
488
6.49k
    if (!this->isReplaceOp()) {
489
6.49k
        switch (fOp) {
490
996
            case SkClipOp::kDifference:
491
996
                this->combineBoundsDiff(combination, prevFinite);
492
996
                break;
493
5.49k
            case SkClipOp::kIntersect:
494
5.49k
                this->combineBoundsIntersection(combination, prevFinite);
495
5.49k
                break;
496
0
            default:
497
0
                SkDebugf("SkClipOp error\n");
498
0
                SkASSERT(0);
499
0
                break;
500
6.49k
        }
501
6.49k
    } // else Replace just ignores everything prior and should already have filled in bounds.
502
6.49k
}
503
504
// This constant determines how many Element's are allocated together as a block in
505
// the deque. As such it needs to balance allocating too much memory vs.
506
// incurring allocation/deallocation thrashing. It should roughly correspond to
507
// the deepest save/restore stack we expect to see.
508
static const int kDefaultElementAllocCnt = 8;
509
510
SkClipStack::SkClipStack()
511
    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
512
0
    , fSaveCount(0) {
513
0
}
514
515
SkClipStack::SkClipStack(void* storage, size_t size)
516
    : fDeque(sizeof(Element), storage, size, kDefaultElementAllocCnt)
517
5.12k
    , fSaveCount(0) {
518
5.12k
}
519
520
SkClipStack::SkClipStack(const SkClipStack& b)
521
0
    : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
522
0
    *this = b;
523
0
}
524
525
5.12k
SkClipStack::~SkClipStack() {
526
5.12k
    reset();
527
5.12k
}
528
529
0
SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
530
0
    if (this == &b) {
531
0
        return *this;
532
0
    }
533
0
    reset();
534
535
0
    fSaveCount = b.fSaveCount;
536
0
    SkDeque::F2BIter recIter(b.fDeque);
537
0
    for (const Element* element = (const Element*)recIter.next();
538
0
         element != nullptr;
539
0
         element = (const Element*)recIter.next()) {
540
0
        new (fDeque.push_back()) Element(*element);
541
0
    }
542
543
0
    return *this;
544
0
}
545
546
0
bool SkClipStack::operator==(const SkClipStack& b) const {
547
0
    if (this->getTopmostGenID() == b.getTopmostGenID()) {
548
0
        return true;
549
0
    }
550
0
    if (fSaveCount != b.fSaveCount ||
551
0
        fDeque.count() != b.fDeque.count()) {
552
0
        return false;
553
0
    }
554
0
    SkDeque::F2BIter myIter(fDeque);
555
0
    SkDeque::F2BIter bIter(b.fDeque);
556
0
    const Element* myElement = (const Element*)myIter.next();
557
0
    const Element* bElement = (const Element*)bIter.next();
558
559
0
    while (myElement != nullptr && bElement != nullptr) {
560
0
        if (*myElement != *bElement) {
561
0
            return false;
562
0
        }
563
0
        myElement = (const Element*)myIter.next();
564
0
        bElement = (const Element*)bIter.next();
565
0
    }
566
0
    return myElement == nullptr && bElement == nullptr;
567
0
}
568
569
5.12k
void SkClipStack::reset() {
570
    // We used a placement new for each object in fDeque, so we're responsible
571
    // for calling the destructor on each of them as well.
572
6.38k
    while (!fDeque.empty()) {
573
1.26k
        Element* element = (Element*)fDeque.back();
574
1.26k
        element->~Element();
575
1.26k
        fDeque.pop_back();
576
1.26k
    }
577
578
5.12k
    fSaveCount = 0;
579
5.12k
}
580
581
6.43k
void SkClipStack::save() {
582
6.43k
    fSaveCount += 1;
583
6.43k
}
584
585
6.43k
void SkClipStack::restore() {
586
6.43k
    fSaveCount -= 1;
587
6.43k
    restoreTo(fSaveCount);
588
6.43k
}
589
590
6.43k
void SkClipStack::restoreTo(int saveCount) {
591
11.6k
    while (!fDeque.empty()) {
592
6.90k
        Element* element = (Element*)fDeque.back();
593
6.90k
        if (element->fSaveCount <= saveCount) {
594
1.68k
            break;
595
1.68k
        }
596
5.21k
        element->~Element();
597
5.21k
        fDeque.pop_back();
598
5.21k
    }
599
6.43k
}
600
601
50.2k
SkRect SkClipStack::bounds(const SkIRect& deviceBounds) const {
602
    // TODO: optimize this.
603
50.2k
    SkRect r;
604
50.2k
    SkClipStack::BoundsType bounds;
605
50.2k
    this->getBounds(&r, &bounds);
606
50.2k
    if (bounds == SkClipStack::kInsideOut_BoundsType) {
607
41.0k
        return SkRect::Make(deviceBounds);
608
41.0k
    }
609
9.14k
    return r.intersect(SkRect::Make(deviceBounds)) ? r : SkRect::MakeEmpty();
610
50.2k
}
611
612
// TODO: optimize this.
613
25.1k
bool SkClipStack::isEmpty(const SkIRect& r) const { return this->bounds(r).isEmpty(); }
614
615
void SkClipStack::getBounds(SkRect* canvFiniteBound,
616
                            BoundsType* boundType,
617
50.2k
                            bool* isIntersectionOfRects) const {
618
50.2k
    SkASSERT(canvFiniteBound && boundType);
619
620
50.2k
    const Element* element = (const Element*)fDeque.back();
621
622
50.2k
    if (nullptr == element) {
623
        // the clip is wide open - the infinite plane w/ no pixels un-writeable
624
36.3k
        canvFiniteBound->setEmpty();
625
36.3k
        *boundType = kInsideOut_BoundsType;
626
36.3k
        if (isIntersectionOfRects) {
627
0
            *isIntersectionOfRects = false;
628
0
        }
629
36.3k
        return;
630
36.3k
    }
631
632
13.8k
    *canvFiniteBound = element->fFiniteBound;
633
13.8k
    *boundType = element->fFiniteBoundType;
634
13.8k
    if (isIntersectionOfRects) {
635
0
        *isIntersectionOfRects = element->fIsIntersectionOfRects;
636
0
    }
637
13.8k
}
638
639
0
bool SkClipStack::internalQuickContains(const SkRect& rect) const {
640
0
    Iter iter(*this, Iter::kTop_IterStart);
641
0
    const Element* element = iter.prev();
642
0
    while (element != nullptr) {
643
        // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
644
0
        if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
645
0
            return false;
646
0
        }
647
0
        if (element->isInverseFilled()) {
648
            // Part of 'rect' could be trimmed off by the inverse-filled clip element
649
0
            if (SkRect::Intersects(element->getBounds(), rect)) {
650
0
                return false;
651
0
            }
652
0
        } else {
653
0
            if (!element->contains(rect)) {
654
0
                return false;
655
0
            }
656
0
        }
657
0
        if (element->isReplaceOp()) {
658
0
            break;
659
0
        }
660
0
        element = iter.prev();
661
0
    }
662
0
    return true;
663
0
}
664
665
0
bool SkClipStack::internalQuickContains(const SkRRect& rrect) const {
666
0
    Iter iter(*this, Iter::kTop_IterStart);
667
0
    const Element* element = iter.prev();
668
0
    while (element != nullptr) {
669
        // TODO: Once expanding ops are removed, this condition is equiv. to op == kDifference.
670
0
        if (SkClipOp::kIntersect != element->getOp() && !element->isReplaceOp()) {
671
0
            return false;
672
0
        }
673
0
        if (element->isInverseFilled()) {
674
            // Part of 'rrect' could be trimmed off by the inverse-filled clip element
675
0
            if (SkRect::Intersects(element->getBounds(), rrect.getBounds())) {
676
0
                return false;
677
0
            }
678
0
        } else {
679
0
            if (!element->contains(rrect)) {
680
0
                return false;
681
0
            }
682
0
        }
683
0
        if (element->isReplaceOp()) {
684
0
            break;
685
0
        }
686
0
        element = iter.prev();
687
0
    }
688
0
    return true;
689
0
}
690
691
7.09k
void SkClipStack::pushElement(const Element& element) {
692
    // Use reverse iterator instead of back because Rect path may need previous
693
7.09k
    SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
694
7.09k
    Element* prior = (Element*) iter.prev();
695
696
7.09k
    if (prior) {
697
2.09k
        if (element.isReplaceOp()) {
698
0
            this->restoreTo(fSaveCount - 1);
699
0
            prior = (Element*) fDeque.back();
700
2.09k
        } else if (prior->canBeIntersectedInPlace(fSaveCount, element.getOp())) {
701
848
            switch (prior->fDeviceSpaceType) {
702
584
                case Element::DeviceSpaceType::kEmpty:
703
584
                    SkDEBUGCODE(prior->checkEmpty();)
704
584
                    return;
705
0
                case Element::DeviceSpaceType::kShader:
706
0
                    if (Element::DeviceSpaceType::kShader == element.getDeviceSpaceType()) {
707
0
                        prior->fShader = SkShaders::Blend(SkBlendMode::kSrcIn,
708
0
                                                          element.fShader, prior->fShader);
709
0
                        Element* priorPrior = (Element*) iter.prev();
710
0
                        prior->updateBoundAndGenID(priorPrior);
711
0
                        return;
712
0
                    }
713
0
                    break;
714
46
                case Element::DeviceSpaceType::kRect:
715
46
                    if (Element::DeviceSpaceType::kRect == element.getDeviceSpaceType()) {
716
8
                        if (prior->rectRectIntersectAllowed(element.getDeviceSpaceRect(),
717
8
                                                            element.isAA())) {
718
8
                            SkRect isectRect;
719
8
                            if (!isectRect.intersect(prior->getDeviceSpaceRect(),
720
8
                                                     element.getDeviceSpaceRect())) {
721
1
                                prior->setEmpty();
722
1
                                return;
723
1
                            }
724
725
7
                            prior->fDeviceSpaceRRect.setRect(isectRect);
726
7
                            prior->fDoAA = element.isAA();
727
7
                            Element* priorPrior = (Element*) iter.prev();
728
7
                            prior->updateBoundAndGenID(priorPrior);
729
7
                            return;
730
8
                        }
731
0
                        break;
732
8
                    }
733
46
                    [[fallthrough]];
734
256
                default:
735
256
                    if (!SkRect::Intersects(prior->getBounds(), element.getBounds())) {
736
16
                        prior->setEmpty();
737
16
                        return;
738
16
                    }
739
240
                    break;
740
848
            }
741
848
        }
742
2.09k
    }
743
6.48k
    Element* newElement = new (fDeque.push_back()) Element(element);
744
6.48k
    newElement->updateBoundAndGenID(prior);
745
6.48k
}
746
747
530
void SkClipStack::clipRRect(const SkRRect& rrect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
748
530
    Element element(fSaveCount, rrect, matrix, op, doAA);
749
530
    this->pushElement(element);
750
530
}
751
752
5.38k
void SkClipStack::clipRect(const SkRect& rect, const SkMatrix& matrix, SkClipOp op, bool doAA) {
753
5.38k
    Element element(fSaveCount, rect, matrix, op, doAA);
754
5.38k
    this->pushElement(element);
755
5.38k
}
756
757
void SkClipStack::clipPath(const SkPath& path, const SkMatrix& matrix, SkClipOp op,
758
1.18k
                           bool doAA) {
759
1.18k
    Element element(fSaveCount, path, matrix, op, doAA);
760
1.18k
    this->pushElement(element);
761
1.18k
}
762
763
0
void SkClipStack::clipShader(sk_sp<SkShader> shader) {
764
0
    Element element(fSaveCount, std::move(shader));
765
0
    this->pushElement(element);
766
0
}
767
768
0
void SkClipStack::replaceClip(const SkRect& rect, bool doAA) {
769
0
    Element element(fSaveCount, rect, doAA);
770
0
    this->pushElement(element);
771
0
}
772
773
0
void SkClipStack::clipEmpty() {
774
0
    Element* element = (Element*) fDeque.back();
775
776
0
    if (element && element->canBeIntersectedInPlace(fSaveCount, SkClipOp::kIntersect)) {
777
0
        element->setEmpty();
778
0
    }
779
0
    new (fDeque.push_back()) Element(fSaveCount);
780
781
0
    ((Element*)fDeque.back())->fGenID = kEmptyGenID;
782
0
}
783
784
///////////////////////////////////////////////////////////////////////////////
785
786
0
SkClipStack::Iter::Iter() : fStack(nullptr) {
787
0
}
788
789
SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
790
4.30k
    : fStack(&stack) {
791
4.30k
    this->reset(stack, startLoc);
792
4.30k
}
793
794
5.10k
const SkClipStack::Element* SkClipStack::Iter::next() {
795
5.10k
    return (const SkClipStack::Element*)fIter.next();
796
5.10k
}
797
798
0
const SkClipStack::Element* SkClipStack::Iter::prev() {
799
0
    return (const SkClipStack::Element*)fIter.prev();
800
0
}
801
802
0
const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkClipOp op) {
803
0
    if (nullptr == fStack) {
804
0
        return nullptr;
805
0
    }
806
807
0
    fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
808
809
0
    const SkClipStack::Element* element = nullptr;
810
811
0
    for (element = (const SkClipStack::Element*) fIter.prev();
812
0
         element;
813
0
         element = (const SkClipStack::Element*) fIter.prev()) {
814
815
0
        if (op == element->fOp) {
816
            // The Deque's iterator is actually one pace ahead of the
817
            // returned value. So while "element" is the element we want to
818
            // return, the iterator is actually pointing at (and will
819
            // return on the next "next" or "prev" call) the element
820
            // in front of it in the deque. Bump the iterator forward a
821
            // step so we get the expected result.
822
0
            if (nullptr == fIter.next()) {
823
                // The reverse iterator has run off the front of the deque
824
                // (i.e., the "op" clip is the first clip) and can't
825
                // recover. Reset the iterator to start at the front.
826
0
                fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
827
0
            }
828
0
            break;
829
0
        }
830
0
    }
831
832
0
    if (nullptr == element) {
833
        // There were no "op" clips
834
0
        fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
835
0
    }
836
837
0
    return this->next();
838
0
}
839
840
4.30k
void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
841
4.30k
    fStack = &stack;
842
4.30k
    fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
843
4.30k
}
844
845
// helper method
846
void SkClipStack::getConservativeBounds(int offsetX,
847
                                        int offsetY,
848
                                        int maxWidth,
849
                                        int maxHeight,
850
                                        SkRect* devBounds,
851
0
                                        bool* isIntersectionOfRects) const {
852
0
    SkASSERT(devBounds);
853
854
0
    devBounds->setLTRB(0, 0,
855
0
                       SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
856
857
0
    SkRect temp;
858
0
    SkClipStack::BoundsType boundType;
859
860
    // temp starts off in canvas space here
861
0
    this->getBounds(&temp, &boundType, isIntersectionOfRects);
862
0
    if (SkClipStack::kInsideOut_BoundsType == boundType) {
863
0
        return;
864
0
    }
865
866
    // but is converted to device space here
867
0
    temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
868
869
0
    if (!devBounds->intersect(temp)) {
870
0
        devBounds->setEmpty();
871
0
    }
872
0
}
873
874
0
bool SkClipStack::isRRect(const SkRect& bounds, SkRRect* rrect, bool* aa) const {
875
0
    const Element* back = static_cast<const Element*>(fDeque.back());
876
0
    if (!back) {
877
        // TODO: return bounds?
878
0
        return false;
879
0
    }
880
    // First check if the entire stack is known to be a rect by the top element.
881
0
    if (back->fIsIntersectionOfRects && back->fFiniteBoundType == BoundsType::kNormal_BoundsType) {
882
0
        rrect->setRect(back->fFiniteBound);
883
0
        *aa = back->isAA();
884
0
        return true;
885
0
    }
886
887
0
    if (back->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kRect &&
888
0
        back->getDeviceSpaceType() != SkClipStack::Element::DeviceSpaceType::kRRect) {
889
0
        return false;
890
0
    }
891
0
    if (back->isReplaceOp()) {
892
0
        *rrect = back->asDeviceSpaceRRect();
893
0
        *aa = back->isAA();
894
0
        return true;
895
0
    }
896
897
0
    if (back->getOp() == SkClipOp::kIntersect) {
898
0
        SkRect backBounds;
899
0
        if (!backBounds.intersect(bounds, back->asDeviceSpaceRRect().rect())) {
900
0
            return false;
901
0
        }
902
        // We limit to 17 elements. This means the back element will be bounds checked at most 16
903
        // times if it is an rrect.
904
0
        int cnt = fDeque.count();
905
0
        if (cnt > 17) {
906
0
            return false;
907
0
        }
908
0
        if (cnt > 1) {
909
0
            SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
910
0
            SkAssertResult(static_cast<const Element*>(iter.prev()) == back);
911
0
            while (const Element* prior = (const Element*)iter.prev()) {
912
                // TODO: Once expanding clip ops are removed, this is equiv. to op == kDifference
913
0
                if ((prior->getOp() != SkClipOp::kIntersect && !prior->isReplaceOp()) ||
914
0
                    !prior->contains(backBounds)) {
915
0
                    return false;
916
0
                }
917
0
                if (prior->isReplaceOp()) {
918
0
                    break;
919
0
                }
920
0
            }
921
0
        }
922
0
        *rrect = back->asDeviceSpaceRRect();
923
0
        *aa = back->isAA();
924
0
        return true;
925
0
    }
926
0
    return false;
927
0
}
928
929
6.49k
uint32_t SkClipStack::GetNextGenID() {
930
    // 0-2 are reserved for invalid, empty & wide-open
931
6.49k
    static const uint32_t kFirstUnreservedGenID = 3;
932
6.49k
    static std::atomic<uint32_t> nextID{kFirstUnreservedGenID};
933
934
6.49k
    uint32_t id;
935
6.49k
    do {
936
6.49k
        id = nextID.fetch_add(1, std::memory_order_relaxed);
937
6.49k
    } while (id < kFirstUnreservedGenID);
938
6.49k
    return id;
939
6.49k
}
940
941
0
uint32_t SkClipStack::getTopmostGenID() const {
942
0
    if (fDeque.empty()) {
943
0
        return kWideOpenGenID;
944
0
    }
945
946
0
    const Element* back = static_cast<const Element*>(fDeque.back());
947
0
    if (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty() &&
948
0
        Element::DeviceSpaceType::kShader != back->fDeviceSpaceType) {
949
0
        return kWideOpenGenID;
950
0
    }
951
952
0
    return back->getGenID();
953
0
}
954
955
#ifdef SK_DEBUG
956
void SkClipStack::Element::dump() const {
957
    static const char* kTypeStrings[] = {
958
        "empty",
959
        "rect",
960
        "rrect",
961
        "path",
962
        "shader"
963
    };
964
    static_assert(0 == static_cast<int>(DeviceSpaceType::kEmpty), "enum mismatch");
965
    static_assert(1 == static_cast<int>(DeviceSpaceType::kRect), "enum mismatch");
966
    static_assert(2 == static_cast<int>(DeviceSpaceType::kRRect), "enum mismatch");
967
    static_assert(3 == static_cast<int>(DeviceSpaceType::kPath), "enum mismatch");
968
    static_assert(4 == static_cast<int>(DeviceSpaceType::kShader), "enum mismatch");
969
    static_assert(std::size(kTypeStrings) == kTypeCnt, "enum mismatch");
970
971
    const char* opName = this->isReplaceOp() ? "replace" :
972
            (fOp == SkClipOp::kDifference ? "difference" : "intersect");
973
    SkDebugf("Type: %s, Op: %s, AA: %s, Save Count: %d\n", kTypeStrings[(int)fDeviceSpaceType],
974
             opName, (fDoAA ? "yes" : "no"), fSaveCount);
975
    switch (fDeviceSpaceType) {
976
        case DeviceSpaceType::kEmpty:
977
            SkDebugf("\n");
978
            break;
979
        case DeviceSpaceType::kRect:
980
            this->getDeviceSpaceRect().dump();
981
            SkDebugf("\n");
982
            break;
983
        case DeviceSpaceType::kRRect:
984
            this->getDeviceSpaceRRect().dump();
985
            SkDebugf("\n");
986
            break;
987
        case DeviceSpaceType::kPath:
988
            this->getDeviceSpacePath().dump(nullptr, false);
989
            break;
990
        case DeviceSpaceType::kShader:
991
            // SkShaders don't provide much introspection that's worth while.
992
            break;
993
    }
994
}
995
996
void SkClipStack::dump() const {
997
    B2TIter iter(*this);
998
    const Element* e;
999
    while ((e = iter.next())) {
1000
        e->dump();
1001
        SkDebugf("\n");
1002
    }
1003
}
1004
#endif