Coverage Report

Created: 2024-09-14 07:19

/src/skia/modules/sksg/src/SkSGMaskEffect.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2018 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 "modules/sksg/include/SkSGMaskEffect.h"
9
10
#include "include/core/SkBlendMode.h"
11
#include "include/core/SkCanvas.h"
12
#include "include/core/SkColorFilter.h"
13
#include "include/core/SkPaint.h"
14
#include "include/effects/SkLumaColorFilter.h"
15
#include "include/private/base/SkAssert.h"
16
#include "include/private/base/SkTo.h"
17
#include "modules/sksg/include/SkSGNode.h"
18
19
class SkMatrix;
20
struct SkPoint;
21
22
namespace sksg {
23
24
47.0k
static bool is_inverted(sksg::MaskEffect::Mode mode) {
25
47.0k
    return static_cast<uint32_t>(mode) & 1;
26
47.0k
}
27
28
14.1k
static bool is_luma(sksg::MaskEffect::Mode mode) {
29
14.1k
    return static_cast<uint32_t>(mode) & 2;
30
14.1k
}
31
32
MaskEffect::MaskEffect(sk_sp<RenderNode> child, sk_sp<RenderNode> mask, Mode mode)
33
    : INHERITED(std::move(child))
34
    , fMaskNode(std::move(mask))
35
31.1k
    , fMaskMode(mode) {
36
31.1k
    this->observeInval(fMaskNode);
37
31.1k
}
38
39
31.1k
MaskEffect::~MaskEffect() {
40
31.1k
    this->unobserveInval(fMaskNode);
41
31.1k
}
42
43
14.1k
void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
44
14.1k
    SkAutoCanvasRestore acr(canvas, false);
45
46
    // The mask mode covers two independent bits.
47
    //
48
    //   - mask source controls how the mask coverage is generated:
49
    //     * alpha => coverage = mask_alpha
50
    //     * luma  => coverage = luma(mask_rgb)
51
    //
52
    //   - mask type controls how the mask coverage is interpreted:
53
    //     * normal   => coverage' = coverage
54
    //     * inverted => coverage' = 1 - coverage
55
56
14.1k
    {
57
        // Outer layer: mask coverage stored in the alpha channel.
58
14.1k
        SkPaint mask_layer_paint;
59
14.1k
        if (ctx) {
60
            // Apply all optional context overrides upfront.
61
10.9k
            ctx->modulatePaint(canvas->getTotalMatrix(), &mask_layer_paint);
62
10.9k
        }
63
64
14.1k
        RenderContext mask_render_context;
65
14.1k
        if (is_luma(fMaskMode)) {
66
6.16k
            mask_render_context.fColorFilter = SkLumaColorFilter::Make();
67
6.16k
        }
68
69
        // TODO: could be an A8 layer?
70
14.1k
        canvas->saveLayer(this->bounds(), &mask_layer_paint);
71
14.1k
        fMaskNode->render(canvas, &mask_render_context);
72
73
14.1k
        {
74
            // Inner layer: masked content.
75
14.1k
            SkPaint content_layer_paint;
76
14.1k
            content_layer_paint.setBlendMode(is_inverted(fMaskMode) ? SkBlendMode::kSrcOut
77
14.1k
                                                                    : SkBlendMode::kSrcIn);
78
14.1k
            canvas->saveLayer(this->bounds(), &content_layer_paint);
79
80
14.1k
            this->INHERITED::onRender(canvas, nullptr);
81
14.1k
        }
82
14.1k
    }
83
14.1k
}
84
85
0
const RenderNode* MaskEffect::onNodeAt(const SkPoint& p) const {
86
0
    const auto mask_hit = (SkToBool(fMaskNode->nodeAt(p)) == !is_inverted(fMaskMode));
87
88
0
    if (!mask_hit) {
89
0
        return nullptr;
90
0
    }
91
92
0
    return this->INHERITED::onNodeAt(p);
93
0
}
94
95
32.8k
SkRect MaskEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
96
32.8k
    SkASSERT(this->hasInval());
97
98
32.8k
    const auto maskBounds = fMaskNode->revalidate(ic, ctm);
99
32.8k
    auto childBounds = this->INHERITED::onRevalidate(ic, ctm);
100
101
32.8k
    return (is_inverted(fMaskMode) || childBounds.intersect(maskBounds))
102
32.8k
        ? childBounds
103
32.8k
        : SkRect::MakeEmpty();
104
32.8k
}
105
106
} // namespace sksg