1/* 2 * Copyright 2021 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "modules/svg/include/SkSVGMask.h" 9 10#include "include/core/SkCanvas.h" 11#include "include/effects/SkLumaColorFilter.h" 12#include "modules/svg/include/SkSVGRenderContext.h" 13 14bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { 15 return INHERITED::parseAndSetAttribute(n, v) || 16 this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) || 17 this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) || 18 this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", n, v)) || 19 this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", n, v)) || 20 this->setMaskUnits( 21 SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskUnits", n, v)) || 22 this->setMaskContentUnits( 23 SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskContentUnits", n, v)); 24} 25 26SkRect SkSVGMask::bounds(const SkSVGRenderContext& ctx) const { 27 return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits); 28} 29 30void SkSVGMask::renderMask(const SkSVGRenderContext& ctx) const { 31 // https://www.w3.org/TR/SVG11/masking.html#Masking 32 33 // Propagate any inherited properties that may impact mask effect behavior (e.g. 34 // color-interpolation). We call this explicitly here because the SkSVGMask 35 // nodes do not participate in the normal onRender path, which is when property 36 // propagation currently occurs. 37 // The local context also restores the filter layer created below on scope exit. 38 SkSVGRenderContext lctx(ctx); 39 this->onPrepareToRender(&lctx); 40 41 const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation; 42 auto ci_filter = (ci == SkSVGColorspace::kLinearRGB) 43 ? SkColorFilters::SRGBToLinearGamma() 44 : nullptr; 45 46 SkPaint mask_filter; 47 mask_filter.setColorFilter( 48 SkColorFilters::Compose(SkLumaColorFilter::Make(), std::move(ci_filter))); 49 50 // Mask color filter layer. 51 // Note: We could avoid this extra layer if we invert the stacking order 52 // (mask/content -> content/mask, kSrcIn -> kDstIn) and apply the filter 53 // via the top (mask) layer paint. That requires deferring mask rendering 54 // until after node content, which introduces extra state/complexity. 55 // Something to consider if masking performance ever becomes an issue. 56 lctx.canvas()->saveLayer(nullptr, &mask_filter); 57 58 const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits); 59 lctx.canvas()->translate(obbt.offset.x, obbt.offset.y); 60 lctx.canvas()->scale(obbt.scale.x, obbt.scale.y); 61 62 for (const auto& child : fChildren) { 63 child->render(lctx); 64 } 65} 66