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