blob: 849ecc3f575bd82bd07b0516c169ee4d649e85d0 [file] [log] [blame]
/*
* Copyright 2021 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/svg/include/SkSVGMask.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkColorFilter.h"
#include "include/effects/SkLumaColorFilter.h"
#include "modules/svg/include/SkSVGRenderContext.h"
bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) {
return INHERITED::parseAndSetAttribute(n, v) ||
this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) ||
this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) ||
this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", n, v)) ||
this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", n, v)) ||
this->setMaskUnits(
SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskUnits", n, v)) ||
this->setMaskContentUnits(
SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskContentUnits", n, v));
}
SkRect SkSVGMask::bounds(const SkSVGRenderContext& ctx) const {
return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits);
}
void SkSVGMask::renderMask(const SkSVGRenderContext& ctx) const {
// https://www.w3.org/TR/SVG11/masking.html#Masking
// Propagate any inherited properties that may impact mask effect behavior (e.g.
// color-interpolation). We call this explicitly here because the SkSVGMask
// nodes do not participate in the normal onRender path, which is when property
// propagation currently occurs.
// The local context also restores the filter layer created below on scope exit.
SkSVGRenderContext lctx(ctx);
this->onPrepareToRender(&lctx);
const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation;
auto ci_filter = (ci == SkSVGColorspace::kLinearRGB)
? SkColorFilters::SRGBToLinearGamma()
: nullptr;
SkPaint mask_filter;
mask_filter.setColorFilter(
SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter)));
// Mask color filter layer.
// Note: We could avoid this extra layer if we invert the stacking order
// (mask/content -> content/mask, kSrcIn -> kDstIn) and apply the filter
// via the top (mask) layer paint. That requires deferring mask rendering
// until after node content, which introduces extra state/complexity.
// Something to consider if masking performance ever becomes an issue.
lctx.canvas()->saveLayer(nullptr, &mask_filter);
const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits);
lctx.canvas()->translate(obbt.offset.x, obbt.offset.y);
lctx.canvas()->scale(obbt.scale.x, obbt.scale.y);
for (const auto& child : fChildren) {
child->render(lctx);
}
}