/* * 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/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("x", n, v)) || this->setY(SkSVGAttributeParser::parse("y", n, v)) || this->setWidth(SkSVGAttributeParser::parse("width", n, v)) || this->setHeight(SkSVGAttributeParser::parse("height", n, v)) || this->setMaskUnits( SkSVGAttributeParser::parse("maskUnits", n, v)) || this->setMaskContentUnits( SkSVGAttributeParser::parse("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); } }