blob: c2095eb8c30ae1b795ac0901003fb3889a3a15a4 [file] [log] [blame]
Tyler Dennistondf208a32020-10-30 16:01:54 -04001/*
2 * Copyright 2020 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 "include/effects/SkImageFilters.h"
Tyler Dennistonb25caae2020-11-09 12:46:02 -05009#include "modules/svg/include/SkSVGAttributeParser.h"
Tyler Dennistondf208a32020-10-30 16:01:54 -040010#include "modules/svg/include/SkSVGFe.h"
Tyler Denniston62a683e2020-12-11 11:47:55 -050011#include "modules/svg/include/SkSVGFilterContext.h"
Tyler Denniston0a145b72021-01-11 10:51:41 -050012#include "modules/svg/include/SkSVGRenderContext.h"
Tyler Dennistondf208a32020-10-30 16:01:54 -040013
14sk_sp<SkImageFilter> SkSVGFe::makeImageFilter(const SkSVGRenderContext& ctx,
Tyler Dennistonb25caae2020-11-09 12:46:02 -050015 const SkSVGFilterContext& fctx) const {
16 return this->onMakeImageFilter(ctx, fctx);
17}
18
Tyler Denniston0a145b72021-01-11 10:51:41 -050019SkRect SkSVGFe::resolveBoundaries(const SkSVGRenderContext& ctx,
20 const SkSVGFilterContext& fctx) const {
21 const auto x = fX.isValid() ? *fX : SkSVGLength(0, SkSVGLength::Unit::kPercentage);
22 const auto y = fY.isValid() ? *fY : SkSVGLength(0, SkSVGLength::Unit::kPercentage);
23 const auto w = fWidth.isValid() ? *fWidth : SkSVGLength(100, SkSVGLength::Unit::kPercentage);
24 const auto h = fHeight.isValid() ? *fHeight : SkSVGLength(100, SkSVGLength::Unit::kPercentage);
25
26 // Resolve the x/y/w/h boundary rect depending on primitiveUnits setting
27 SkRect boundaries;
28 switch (fctx.primitiveUnits().type()) {
29 case SkSVGObjectBoundingBoxUnits::Type::kUserSpaceOnUse:
30 boundaries = ctx.lengthContext().resolveRect(x, y, w, h);
31 break;
32 case SkSVGObjectBoundingBoxUnits::Type::kObjectBoundingBox: {
33 SkASSERT(ctx.node());
34 const SkRect objBounds = ctx.node()->objectBoundingBox(ctx);
35 boundaries = SkSVGLengthContext({1, 1}).resolveRect(x, y, w, h);
36 boundaries = SkRect::MakeXYWH(objBounds.fLeft + boundaries.fLeft * objBounds.width(),
37 objBounds.fTop + boundaries.fTop * objBounds.height(),
38 boundaries.width() * objBounds.width(),
39 boundaries.height() * objBounds.height());
40
41 break;
42 }
43 }
44
45 return boundaries;
46}
47
Tyler Denniston94730fe2021-01-14 15:53:32 -050048static bool AnyIsStandardInput(const std::vector<SkSVGFeInputType>& inputs) {
49 for (const auto& in : inputs) {
50 if (in.type() != SkSVGFeInputType::Type::kFilterPrimitiveReference) {
51 return true;
52 }
53 }
54 return false;
55}
56
Tyler Denniston62a683e2020-12-11 11:47:55 -050057SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx,
58 const SkSVGFilterContext& fctx) const {
Tyler Denniston0a145b72021-01-11 10:51:41 -050059 // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion,
60 // the default filter effect subregion is equal to the union of the subregions defined
61 // for all "referenced nodes" (filter effect inputs). If there are no inputs, the
62 // default subregion is equal to the filter effects region
63 // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion).
64 const std::vector<SkSVGFeInputType> inputs = this->getInputs();
65 SkRect subregion;
Tyler Denniston94730fe2021-01-14 15:53:32 -050066 if (inputs.empty() || AnyIsStandardInput(inputs)) {
Tyler Denniston0a145b72021-01-11 10:51:41 -050067 subregion = fctx.filterEffectsRegion();
68 } else {
69 subregion = fctx.filterPrimitiveSubregion(inputs[0]);
70 for (size_t i = 1; i < inputs.size(); i++) {
71 subregion.join(fctx.filterPrimitiveSubregion(inputs[i]));
72 }
73 }
74
75 // Next resolve the rect specified by the x, y, width, height attributes on this filter effect.
76 // If those attributes were given, they override the corresponding attribute of the default
77 // filter effect subregion calculated above.
78 const SkRect boundaries = this->resolveBoundaries(ctx, fctx);
79 if (fX.isValid()) {
80 subregion.fLeft = boundaries.fLeft;
81 }
82 if (fY.isValid()) {
83 subregion.fTop = boundaries.fTop;
84 }
85 if (fWidth.isValid()) {
86 subregion.fRight = subregion.fLeft + boundaries.width();
87 }
88 if (fHeight.isValid()) {
89 subregion.fBottom = subregion.fTop + boundaries.height();
90 }
91
92 return subregion;
Tyler Denniston62a683e2020-12-11 11:47:55 -050093}
94
Tyler Denniston7bb85db2021-01-13 12:08:04 -050095SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const {
Tyler Denniston8f78d552021-01-14 14:23:13 +000096 constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB;
97 const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters;
98 return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs;
Tyler Denniston7bb85db2021-01-13 12:08:04 -050099}
100
101void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }
102
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500103bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
104 return INHERITED::parseAndSetAttribute(name, value) ||
105 this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
Tyler Denniston62a683e2020-12-11 11:47:55 -0500106 this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value)) ||
107 this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
108 this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
109 this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", name, value)) ||
110 this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", name, value));
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500111}
112
113template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
114 static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
115 {"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
116 {"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
117 {"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
118 {"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
119 {"FillPaint", SkSVGFeInputType::Type::kFillPaint},
120 {"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
121 };
122
123 SkSVGStringType resultId;
124 SkSVGFeInputType::Type t;
125 bool parsedValue = false;
126 if (this->parseEnumMap(gTypeMap, &t)) {
127 *type = SkSVGFeInputType(t);
128 parsedValue = true;
129 } else if (parse(&resultId)) {
130 *type = SkSVGFeInputType(resultId);
131 parsedValue = true;
132 }
133
134 return parsedValue && this->parseEOSToken();
Tyler Dennistondf208a32020-10-30 16:01:54 -0400135}