| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "modules/sksg/include/SkSGMaskEffect.h" |
| |
| #include "include/core/SkCanvas.h" |
| #include "include/effects/SkLumaColorFilter.h" |
| |
| namespace sksg { |
| |
| static bool is_inverted(sksg::MaskEffect::Mode mode) { |
| return static_cast<uint32_t>(mode) & 1; |
| }; |
| |
| static bool is_luma(sksg::MaskEffect::Mode mode) { |
| return static_cast<uint32_t>(mode) & 2; |
| } |
| |
| MaskEffect::MaskEffect(sk_sp<RenderNode> child, sk_sp<RenderNode> mask, Mode mode) |
| : INHERITED(std::move(child)) |
| , fMaskNode(std::move(mask)) |
| , fMaskMode(mode) { |
| this->observeInval(fMaskNode); |
| } |
| |
| MaskEffect::~MaskEffect() { |
| this->unobserveInval(fMaskNode); |
| } |
| |
| void MaskEffect::onRender(SkCanvas* canvas, const RenderContext* ctx) const { |
| SkAutoCanvasRestore acr(canvas, false); |
| |
| // The mask mode covers two independent bits. |
| // |
| // - mask source controls how the mask coverage is generated: |
| // * alpha => coverage = mask_alpha |
| // * luma => coverage = luma(mask_rgb) |
| // |
| // - mask type controls how the mask coverage is interpreted: |
| // * normal => coverage' = coverage |
| // * inverted => coverage' = 1 - coverage |
| |
| { |
| // Outer layer: mask coverage stored in the alpha channel. |
| SkPaint mask_layer_paint; |
| if (ctx) { |
| // Apply all optional context overrides upfront. |
| ctx->modulatePaint(canvas->getTotalMatrix(), &mask_layer_paint); |
| } |
| |
| RenderContext mask_render_context; |
| if (is_luma(fMaskMode)) { |
| mask_render_context.fColorFilter = SkLumaColorFilter::Make(); |
| } |
| |
| // TODO: could be an A8 layer? |
| canvas->saveLayer(this->bounds(), &mask_layer_paint); |
| fMaskNode->render(canvas, &mask_render_context); |
| |
| { |
| // Inner layer: masked content. |
| SkPaint content_layer_paint; |
| content_layer_paint.setBlendMode(is_inverted(fMaskMode) ? SkBlendMode::kSrcOut |
| : SkBlendMode::kSrcIn); |
| canvas->saveLayer(this->bounds(), &content_layer_paint); |
| |
| this->INHERITED::onRender(canvas, nullptr); |
| } |
| } |
| } |
| |
| const RenderNode* MaskEffect::onNodeAt(const SkPoint& p) const { |
| const auto mask_hit = (SkToBool(fMaskNode->nodeAt(p)) == !is_inverted(fMaskMode)); |
| |
| if (!mask_hit) { |
| return nullptr; |
| } |
| |
| return this->INHERITED::onNodeAt(p); |
| } |
| |
| SkRect MaskEffect::onRevalidate(InvalidationController* ic, const SkMatrix& ctm) { |
| SkASSERT(this->hasInval()); |
| |
| const auto maskBounds = fMaskNode->revalidate(ic, ctm); |
| auto childBounds = this->INHERITED::onRevalidate(ic, ctm); |
| |
| return (is_inverted(fMaskMode) || childBounds.intersect(maskBounds)) |
| ? childBounds |
| : SkRect::MakeEmpty(); |
| } |
| |
| } // namespace sksg |