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 | |
15 | static bool is_inverted(sksg::MaskEffect::Mode mode) { |
16 | return static_cast<uint32_t>(mode) & 1; |
17 | }; |
18 | |
19 | static bool is_luma(sksg::MaskEffect::Mode mode) { |
20 | return static_cast<uint32_t>(mode) & 2; |
21 | } |
22 | |
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 | |
30 | MaskEffect::~MaskEffect() { |
31 | this->unobserveInval(fMaskNode); |
32 | } |
33 | |
34 | 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 | |
76 | 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 | |
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 | |