blob: aa2b66c93628e63c26e2af3ebbcd5d4370ecaf1a [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 Denniston62a683e2020-12-11 11:47:55 -050048SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx,
49 const SkSVGFilterContext& fctx) const {
Tyler Denniston0a145b72021-01-11 10:51:41 -050050 // From https://www.w3.org/TR/SVG11/filters.html#FilterPrimitiveSubRegion,
51 // the default filter effect subregion is equal to the union of the subregions defined
52 // for all "referenced nodes" (filter effect inputs). If there are no inputs, the
53 // default subregion is equal to the filter effects region
54 // (https://www.w3.org/TR/SVG11/filters.html#FilterEffectsRegion).
55 const std::vector<SkSVGFeInputType> inputs = this->getInputs();
56 SkRect subregion;
57 if (inputs.empty()) {
58 subregion = fctx.filterEffectsRegion();
59 } else {
60 subregion = fctx.filterPrimitiveSubregion(inputs[0]);
61 for (size_t i = 1; i < inputs.size(); i++) {
62 subregion.join(fctx.filterPrimitiveSubregion(inputs[i]));
63 }
64 }
65
66 // Next resolve the rect specified by the x, y, width, height attributes on this filter effect.
67 // If those attributes were given, they override the corresponding attribute of the default
68 // filter effect subregion calculated above.
69 const SkRect boundaries = this->resolveBoundaries(ctx, fctx);
70 if (fX.isValid()) {
71 subregion.fLeft = boundaries.fLeft;
72 }
73 if (fY.isValid()) {
74 subregion.fTop = boundaries.fTop;
75 }
76 if (fWidth.isValid()) {
77 subregion.fRight = subregion.fLeft + boundaries.width();
78 }
79 if (fHeight.isValid()) {
80 subregion.fBottom = subregion.fTop + boundaries.height();
81 }
82
83 return subregion;
Tyler Denniston62a683e2020-12-11 11:47:55 -050084}
85
Tyler Denniston7bb85db2021-01-13 12:08:04 -050086SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const {
Tyler Denniston8f78d552021-01-14 14:23:13 +000087 constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB;
88 const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters;
89 return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs;
Tyler Denniston7bb85db2021-01-13 12:08:04 -050090}
91
92void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); }
93
Tyler Dennistonb25caae2020-11-09 12:46:02 -050094bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) {
95 return INHERITED::parseAndSetAttribute(name, value) ||
96 this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) ||
Tyler Denniston62a683e2020-12-11 11:47:55 -050097 this->setResult(SkSVGAttributeParser::parse<SkSVGStringType>("result", name, value)) ||
98 this->setX(SkSVGAttributeParser::parse<SkSVGLength>("x", name, value)) ||
99 this->setY(SkSVGAttributeParser::parse<SkSVGLength>("y", name, value)) ||
100 this->setWidth(SkSVGAttributeParser::parse<SkSVGLength>("width", name, value)) ||
101 this->setHeight(SkSVGAttributeParser::parse<SkSVGLength>("height", name, value));
Tyler Dennistonb25caae2020-11-09 12:46:02 -0500102}
103
104template <> bool SkSVGAttributeParser::parse(SkSVGFeInputType* type) {
105 static constexpr std::tuple<const char*, SkSVGFeInputType::Type> gTypeMap[] = {
106 {"SourceGraphic", SkSVGFeInputType::Type::kSourceGraphic},
107 {"SourceAlpha", SkSVGFeInputType::Type::kSourceAlpha},
108 {"BackgroundImage", SkSVGFeInputType::Type::kBackgroundImage},
109 {"BackgroundAlpha", SkSVGFeInputType::Type::kBackgroundAlpha},
110 {"FillPaint", SkSVGFeInputType::Type::kFillPaint},
111 {"StrokePaint", SkSVGFeInputType::Type::kStrokePaint},
112 };
113
114 SkSVGStringType resultId;
115 SkSVGFeInputType::Type t;
116 bool parsedValue = false;
117 if (this->parseEnumMap(gTypeMap, &t)) {
118 *type = SkSVGFeInputType(t);
119 parsedValue = true;
120 } else if (parse(&resultId)) {
121 *type = SkSVGFeInputType(resultId);
122 parsedValue = true;
123 }
124
125 return parsedValue && this->parseEOSToken();
Tyler Dennistondf208a32020-10-30 16:01:54 -0400126}