| /* | |
| * Copyright 2021 Google LLC | |
| * | |
| * 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/core/SkM44.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/effects/SkLumaColorFilter.h" | |
| #include "include/private/SkTArray.h" | |
| #include "modules/svg/include/SkSVGAttribute.h" | |
| #include "modules/svg/include/SkSVGAttributeParser.h" | |
| #include "modules/svg/include/SkSVGRenderContext.h" | |
| #include <utility> | |
| 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); | |
| } | |
| } |