xref: /aosp_15_r20/external/skia/modules/sksg/src/SkSGMaskEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
is_inverted(sksg::MaskEffect::Mode mode)24 static bool is_inverted(sksg::MaskEffect::Mode mode) {
25     return static_cast<uint32_t>(mode) & 1;
26 }
27 
is_luma(sksg::MaskEffect::Mode mode)28 static bool is_luma(sksg::MaskEffect::Mode mode) {
29     return static_cast<uint32_t>(mode) & 2;
30 }
31 
MaskEffect(sk_sp<RenderNode> child,sk_sp<RenderNode> mask,Mode mode)32 MaskEffect::MaskEffect(sk_sp<RenderNode> child, sk_sp<RenderNode> mask, Mode mode)
33     : INHERITED(std::move(child))
34     , fMaskNode(std::move(mask))
35     , fMaskMode(mode) {
36     this->observeInval(fMaskNode);
37 }
38 
~MaskEffect()39 MaskEffect::~MaskEffect() {
40     this->unobserveInval(fMaskNode);
41 }
42 
onRender(SkCanvas * canvas,const RenderContext * ctx) const43 void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
44     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     {
57         // Outer layer: mask coverage stored in the alpha channel.
58         SkPaint mask_layer_paint;
59         if (ctx) {
60             // Apply all optional context overrides upfront.
61             ctx->modulatePaint(canvas->getTotalMatrix(), &mask_layer_paint);
62         }
63 
64         RenderContext mask_render_context;
65         if (is_luma(fMaskMode)) {
66             mask_render_context.fColorFilter = SkLumaColorFilter::Make();
67         }
68 
69         // TODO: could be an A8 layer?
70         canvas->saveLayer(this->bounds(), &mask_layer_paint);
71         fMaskNode->render(canvas, &mask_render_context);
72 
73         {
74             // Inner layer: masked content.
75             SkPaint content_layer_paint;
76             content_layer_paint.setBlendMode(is_inverted(fMaskMode) ? SkBlendMode::kSrcOut
77                                                                     : SkBlendMode::kSrcIn);
78             canvas->saveLayer(this->bounds(), &content_layer_paint);
79 
80             this->INHERITED::onRender(canvas, nullptr);
81         }
82     }
83 }
84 
onNodeAt(const SkPoint & p) const85 const RenderNode* MaskEffect::onNodeAt(const SkPoint& p) const {
86     const auto mask_hit = (SkToBool(fMaskNode->nodeAt(p)) == !is_inverted(fMaskMode));
87 
88     if (!mask_hit) {
89         return nullptr;
90     }
91 
92     return this->INHERITED::onNodeAt(p);
93 }
94 
onRevalidate(InvalidationController * ic,const SkMatrix & ctm)95 SkRect MaskEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) {
96     SkASSERT(this->hasInval());
97 
98     const auto maskBounds = fMaskNode->revalidate(ic, ctm);
99     auto childBounds = this->INHERITED::onRevalidate(ic, ctm);
100 
101     return (is_inverted(fMaskMode) || childBounds.intersect(maskBounds))
102         ? childBounds
103         : SkRect::MakeEmpty();
104 }
105 
106 } // namespace sksg
107