blob: 4aebb57a961e631ec5955601eff9798bc70161a9 [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 Dennistonbd916602021-01-22 13:00:31 -050048static bool AnyIsStandardInput(const SkSVGFilterContext& fctx,
49 const std::vector<SkSVGFeInputType>& inputs) {
Tyler Denniston94730fe2021-01-14 15:53:32 -050050 for (const auto& in : inputs) {
Tyler Dennistonbd916602021-01-22 13:00:31 -050051 switch (in.type()) {
52 case SkSVGFeInputType::Type::kFilterPrimitiveReference:
53 break;
54 case SkSVGFeInputType::Type::kSourceGraphic:
55 case SkSVGFeInputType::Type::kSourceAlpha:
56 case SkSVGFeInputType::Type::kBackgroundImage:
57 case SkSVGFeInputType::Type::kBackgroundAlpha:
58 case SkSVGFeInputType::Type::kFillPaint:
59 case SkSVGFeInputType::Type::kStrokePaint:
60 return true;
61 case SkSVGFeInputType::Type::kUnspecified:
62 // Unspecified means previous result (which may be SourceGraphic).
63 if (fctx.previousResultIsSourceGraphic()) {
64 return true;
65 }
66 break;
Tyler Denniston94730fe2021-01-14 15:53:32 -050067 }
68 }
Tyler Dennistonbd916602021-01-22 13:00:31 -050069
Tyler Denniston94730fe2021-01-14 15:53:32 -050070 return false;
71}
72
Tyler Denniston62a683e2020-12-11 11:47:55 -050073SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx,
74 const SkSVGFilterContext& fctx) const {
Tyler Denniston0a145b72021-01-11 10:51:41 -050075 // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion,
76 // the default filter effect subregion is equal to the union of the subregions defined
77 // for all "referenced nodes" (filter effect inputs). If there are no inputs, the
78 // default subregion is equal to the filter effects region
79 // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion).
80 const std::vector<SkSVGFeInputType> inputs = this->getInputs();
81 SkRect subregion;
Tyler Dennistonbd916602021-01-22 13:00:31 -050082 if (inputs.empty() || AnyIsStandardInput(fctx, inputs)) {
Tyler Denniston0a145b72021-01-11 10:51:41 -050083 subregion = fctx.filterEffectsRegion();
84 } else {
85 subregion = fctx.filterPrimitiveSubregion(inputs[0]);
86 for (size_t i = 1; i < inputs.size(); i++) {
87 subregion.join(fctx.filterPrimitiveSubregion(inputs[i]));
88 }
89 }
90
91 // Next resolve the rect specified by the x, y, width, height attributes on this filter effect.
92 // If those attributes were given, they override the corresponding attribute of the default
93 // filter effect subregion calculated above.
94 const SkRect boundaries = this->resolveBoundaries(ctx, fctx);
95 if (fX.isValid()) {
96 subregion.fLeft = boundaries.fLeft;
97 }
98 if (fY.isValid()) {
99 subregion.fTop = boundaries.fTop;
100 }
101 if (fWidth.isValid()) {
102 subregion.fRight = subregion.fLeft + boundaries.width();
103 }
104 if (fHeight.isValid()) {
105 subregion.fBottom = subregion.fTop + boundaries.height();
106 }
107
108 return subregion;
Tyler Denniston62a683e2020-12-11 11:47:55 -0500109}
110
Tyler Dennistonc7e48242021-01-20 17:31:36 -0500111SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx,
112 const SkSVGFilterContext&) const {
Tyler Denniston8f78d552021-01-14 14:23:13 +0000113 constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB;
114 const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters;
115 return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs;
Tyler Denniston7bb85db2021-01-13 12:08:04 -0500116}
117
118void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }
119
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500120bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
121 return INHERITED::parseAndSetAttribute(name, value) ||
122 this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
Tyler Denniston62a683e2020-12-11 11:47:55 -0500123 this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value)) ||
124 this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
125 this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
126 this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", name, value)) ||
127 this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", name, value));
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500128}
129
130template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
131 static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
132 {"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
133 {"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
134 {"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
135 {"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
136 {"FillPaint", SkSVGFeInputType::Type::kFillPaint},
137 {"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
138 };
139
140 SkSVGStringType resultId;
141 SkSVGFeInputType::Type t;
142 bool parsedValue = false;
143 if (this->parseEnumMap(gTypeMap, &t)) {
144 *type = SkSVGFeInputType(t);
145 parsedValue = true;
146 } else if (parse(&resultId)) {
147 *type = SkSVGFeInputType(resultId);
148 parsedValue = true;
149 }
150
151 return parsedValue && this->parseEOSToken();
Tyler Dennistondf208a32020-10-30 16:01:54 -0400152}