blob: 10646d4fba37e68b08110adcc7d726ca7551262c [file] [log] [blame]
Florin Malita5f9102f2018-01-10 13:36:22 -05001/*
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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "modules/sksg/include/SkSGMaskEffect.h"
Florin Malita5f9102f2018-01-10 13:36:22 -05009
Mike Kleinc0bd9f92019-04-23 12:05:21 -050010#include "include/core/SkCanvas.h"
Florin Malita59e72b72019-10-16 08:51:09 -040011#include "include/effects/SkLumaColorFilter.h"
Florin Malita5f9102f2018-01-10 13:36:22 -050012
13namespace sksg {
14
Florin Malita59e72b72019-10-16 08:51:09 -040015static bool is_inverted(sksg::MaskEffect::Mode mode) {
16 return static_cast<uint32_t>(mode) & 1;
17};
18
19static bool is_luma(sksg::MaskEffect::Mode mode) {
20 return static_cast<uint32_t>(mode) & 2;
21}
22
Florin Malitaa016be92018-03-05 14:01:41 -050023MaskEffect::MaskEffect(sk_sp<RenderNode> child, sk_sp<RenderNode> mask, Mode mode)
Florin Malita5f9102f2018-01-10 13:36:22 -050024 : INHERITED(std::move(child))
Florin Malitaa016be92018-03-05 14:01:41 -050025 , fMaskNode(std::move(mask))
26 , fMaskMode(mode) {
Florin Malita3ba3fa72018-01-22 10:19:28 -050027 this->observeInval(fMaskNode);
Florin Malita5f9102f2018-01-10 13:36:22 -050028}
29
30MaskEffect::~MaskEffect() {
Florin Malita3ba3fa72018-01-22 10:19:28 -050031 this->unobserveInval(fMaskNode);
Florin Malita5f9102f2018-01-10 13:36:22 -050032}
33
Florin Malitac0132ff2018-08-09 07:40:01 -040034void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const {
Florin Malita5f9102f2018-01-10 13:36:22 -050035 SkAutoCanvasRestore acr(canvas, false);
36
Florin Malita59e72b72019-10-16 08:51:09 -040037 // 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 }
Florin Malitae359aa32019-09-04 08:18:32 -040073 }
Florin Malita5f9102f2018-01-10 13:36:22 -050074}
75
Florin Malitaeb46bd82019-02-12 09:33:21 -050076const RenderNode* MaskEffect::onNodeAt(const SkPoint& p) const {
Florin Malita59e72b72019-10-16 08:51:09 -040077 const auto mask_hit = (SkToBool(fMaskNode->nodeAt(p)) == !is_inverted(fMaskMode));
Florin Malitaeb46bd82019-02-12 09:33:21 -050078
Florin Malita59e72b72019-10-16 08:51:09 -040079 if (!mask_hit) {
80 return nullptr;
81 }
82
83 return this->INHERITED::onNodeAt(p);
Florin Malitaeb46bd82019-02-12 09:33:21 -050084}
Florin Malita5f9102f2018-01-10 13:36:22 -050085
86SkRect 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
Florin Malita59e72b72019-10-16 08:51:09 -040092 return (is_inverted(fMaskMode) || childBounds.intersect(maskBounds))
Florin Malitaa016be92018-03-05 14:01:41 -050093 ? childBounds
94 : SkRect::MakeEmpty();
Florin Malita5f9102f2018-01-10 13:36:22 -050095}
96
97} // namespace sksg