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