blob: 9a6ce32a63d2a3fd4797f950f8ff2a36a4a9595e [file] [log] [blame]
/*
* Copyright 2017 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/SkSGRenderNode.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkImageFilter.h"
#include "include/core/SkPaint.h"
#include "modules/sksg/src/SkSGNodePriv.h"
namespace sksg {
namespace {
enum Flags : uint8_t {
kInvisible_Flag = 1 << 0,
};
} // namespace
RenderNode::RenderNode(uint32_t inval_traits) : INHERITED(inval_traits) {}
bool RenderNode::isVisible() const {
return !(fNodeFlags & kInvisible_Flag);
}
void RenderNode::setVisible(bool v) {
if (v == this->isVisible()) {
return;
}
this->invalidate();
fNodeFlags = v ? (fNodeFlags & ~kInvisible_Flag)
: (fNodeFlags | kInvisible_Flag);
}
void RenderNode::render(SkCanvas* canvas, const RenderContext* ctx) const {
SkASSERT(!this->hasInval());
if (this->isVisible() && !this->bounds().isEmpty()) {
this->onRender(canvas, ctx);
}
SkASSERT(!this->hasInval());
}
const RenderNode* RenderNode::nodeAt(const SkPoint& p) const {
return this->bounds().contains(p.x(), p.y()) ? this->onNodeAt(p) : nullptr;
}
static SkAlpha ScaleAlpha(SkAlpha alpha, float opacity) {
return SkToU8(sk_float_round2int(alpha * opacity));
}
static sk_sp<SkShader> LocalShader(const sk_sp<SkShader> shader,
const SkMatrix& base,
const SkMatrix& ctm) {
// Mask filters / shaders are declared to operate under a specific transform, but due to the
// deferral mechanism, other transformations might have been pushed to the state.
// We want to undo these transforms (T):
//
// baseCTM x T = ctm
//
// => T = Inv(baseCTM) x ctm
//
// => Inv(T) = Inv(Inv(baseCTM) x ctm)
//
// => Inv(T) = Inv(ctm) x baseCTM
SkMatrix lm;
if (base != ctm && ctm.invert(&lm)) {
lm.preConcat(base);
} else {
lm = SkMatrix::I();
}
// Note: this doesn't play ball with existing shader local matrices (what we really want is
// SkShader::makeWithPostLocalMatrix). Probably a good signal that the whole mechanism is
// contrived and should be redesigned (use SkCanvas::clipShader when available, drop shader
// "effects" completely, etc).
return shader->makeWithLocalMatrix(lm);
}
bool RenderNode::RenderContext::requiresIsolation() const {
// Note: fShader is never applied on isolation layers.
return ScaleAlpha(SK_AlphaOPAQUE, fOpacity) != SK_AlphaOPAQUE
|| fColorFilter
|| fMaskShader
|| fBlendMode != SkBlendMode::kSrcOver;
}
void RenderNode::RenderContext::modulatePaint(const SkMatrix& ctm, SkPaint* paint,
bool is_layer_paint) const {
paint->setAlpha(ScaleAlpha(paint->getAlpha(), fOpacity));
paint->setColorFilter(SkColorFilters::Compose(fColorFilter, paint->refColorFilter()));
if (fShader) {
paint->setShader(LocalShader(fShader, fShaderCTM, ctm));
}
if (fBlendMode != SkBlendMode::kSrcOver) {
paint->setBlendMode(fBlendMode);
}
// Only apply the shader mask for regular paints. Isolation layers require
// special handling on restore.
if (!is_layer_paint && fMaskShader) {
paint->setShader(SkShaders::Blend(SkBlendMode::kSrcIn,
LocalShader(fMaskShader, fMaskCTM, ctm),
paint->refShader()));
}
}
RenderNode::ScopedRenderContext::ScopedRenderContext(SkCanvas* canvas, const RenderContext* ctx)
: fCanvas(canvas)
, fCtx(ctx ? *ctx : RenderContext())
, fRestoreCount(canvas->getSaveCount()) {}
RenderNode::ScopedRenderContext::~ScopedRenderContext() {
if (fRestoreCount >= 0) {
if (fMaskShader) {
SkPaint mask_paint;
mask_paint.setBlendMode(SkBlendMode::kDstIn);
mask_paint.setShader(std::move(fMaskShader));
fCanvas->drawPaint(mask_paint);
}
fCanvas->restoreToCount(fRestoreCount);
}
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateOpacity(float opacity) {
SkASSERT(opacity >= 0 && opacity <= 1);
fCtx.fOpacity *= opacity;
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateColorFilter(sk_sp<SkColorFilter> cf) {
fCtx.fColorFilter = SkColorFilters::Compose(std::move(fCtx.fColorFilter), std::move(cf));
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateShader(sk_sp<SkShader> sh, const SkMatrix& shader_ctm) {
// Topmost shader takes precedence.
if (!fCtx.fShader) {
fCtx.fShader = std::move(sh);
fCtx.fShaderCTM = shader_ctm;
}
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateMaskShader(sk_sp<SkShader> ms, const SkMatrix& ctm) {
if (fCtx.fMaskShader) {
// As we compose mask filters, use the relative transform T for the inner mask:
//
// maskCTM x T = ctm
//
// => T = Inv(maskCTM) x ctm
//
SkMatrix invMaskCTM;
if (ms && fCtx.fMaskCTM.invert(&invMaskCTM)) {
const auto relative_transform = SkMatrix::Concat(invMaskCTM, ctm);
fCtx.fMaskShader = SkShaders::Blend(SkBlendMode::kSrcIn,
std::move(fCtx.fMaskShader),
ms->makeWithLocalMatrix(relative_transform));
}
} else {
fCtx.fMaskShader = std::move(ms);
fCtx.fMaskCTM = ctm;
}
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::modulateBlendMode(SkBlendMode mode) {
fCtx.fBlendMode = mode;
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::setIsolation(const SkRect& bounds, const SkMatrix& ctm,
bool isolation) {
if (isolation && fCtx.requiresIsolation()) {
SkPaint layer_paint;
fCtx.modulatePaint(ctm, &layer_paint, /*is_layer_paint = */true);
fCanvas->saveLayer(bounds, &layer_paint);
// Fetch the mask shader for restore.
if (fCtx.fMaskShader) {
fMaskShader = LocalShader(fCtx.fMaskShader, fCtx.fMaskCTM, ctm);
}
// Reset only the props applied via isolation layers.
fCtx.fColorFilter = nullptr;
fCtx.fMaskShader = nullptr;
fCtx.fOpacity = 1;
fCtx.fBlendMode = SkBlendMode::kSrcOver;
}
return std::move(*this);
}
RenderNode::ScopedRenderContext&&
RenderNode::ScopedRenderContext::setFilterIsolation(const SkRect& bounds, const SkMatrix& ctm,
sk_sp<SkImageFilter> filter) {
if (filter) {
SkPaint layer_paint;
fCtx.modulatePaint(ctm, &layer_paint);
SkASSERT(!layer_paint.getImageFilter());
layer_paint.setImageFilter(std::move(filter));
fCanvas->saveLayer(bounds, &layer_paint);
fCtx = RenderContext();
}
return std::move(*this);
}
CustomRenderNode::CustomRenderNode(std::vector<sk_sp<RenderNode>>&& children)
: INHERITED(kOverrideDamage_Trait) // We cannot make any assumptions - override conservatively.
, fChildren(std::move(children)) {
for (const auto& child : fChildren) {
this->observeInval(child);
}
}
CustomRenderNode::~CustomRenderNode() {
for (const auto& child : fChildren) {
this->unobserveInval(child);
}
}
bool CustomRenderNode::hasChildrenInval() const {
for (const auto& child : fChildren) {
if (NodePriv::HasInval(child)) {
return true;
}
}
return false;
}
} // namespace sksg