/src/skia/modules/svg/src/SkSVGMask.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
14 | 1.08k | bool SkSVGMask::parseAndSetAttribute(const char* n, const char* v) { |
15 | 1.08k | return INHERITED::parseAndSetAttribute(n, v) || |
16 | 974 | this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", n, v)) || |
17 | 974 | this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", n, v)) || |
18 | 926 | this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", n, v)) || |
19 | 926 | this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", n, v)) || |
20 | 926 | this->setMaskUnits( |
21 | 926 | SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskUnits", n, v)) || |
22 | 926 | this->setMaskContentUnits( |
23 | 926 | SkSVGAttributeParser::parse<SkSVGObjectBoundingBoxUnits>("maskContentUnits", n, v)); |
24 | 1.08k | } |
25 | | |
26 | 0 | SkRect SkSVGMask::bounds(const SkSVGRenderContext& ctx) const { |
27 | 0 | return ctx.resolveOBBRect(fX, fY, fWidth, fHeight, fMaskUnits); |
28 | 0 | } |
29 | | |
30 | 0 | void 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 | 0 | SkSVGRenderContext lctx(ctx); |
39 | 0 | this->onPrepareToRender(&lctx); |
40 | |
|
41 | 0 | const auto ci = *lctx.presentationContext().fInherited.fColorInterpolation; |
42 | 0 | auto ci_filter = (ci == SkSVGColorspace::kLinearRGB) |
43 | 0 | ? SkColorFilters::SRGBToLinearGamma() |
44 | 0 | : nullptr; |
45 | |
|
46 | 0 | SkPaint mask_filter; |
47 | 0 | mask_filter.setColorFilter( |
48 | 0 | 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 | 0 | lctx.canvas()->saveLayer(nullptr, &mask_filter); |
57 | |
|
58 | 0 | const auto obbt = ctx.transformForCurrentOBB(fMaskContentUnits); |
59 | 0 | lctx.canvas()->translate(obbt.offset.x, obbt.offset.y); |
60 | 0 | lctx.canvas()->scale(obbt.scale.x, obbt.scale.y); |
61 | |
|
62 | 0 | for (const auto& child : fChildren) { |
63 | 0 | child->render(lctx); |
64 | 0 | } |
65 | 0 | } |