blob: 10646d4fba37e68b08110adcc7d726ca7551262c [file] [log] [blame]
/*
* 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