Tyler Denniston | df208a3 | 2020-10-30 16:01:54 -0400 | [diff] [blame] | 1 | /* |
| 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 Denniston | b25caae | 2020-11-09 12:46:02 -0500 | [diff] [blame] | 9 | #include "modules/svg/include/SkSVGAttributeParser.h" |
Tyler Denniston | df208a3 | 2020-10-30 16:01:54 -0400 | [diff] [blame] | 10 | #include "modules/svg/include/SkSVGFe.h" |
Tyler Denniston | 62a683e | 2020-12-11 11:47:55 -0500 | [diff] [blame] | 11 | #include "modules/svg/include/SkSVGFilterContext.h" |
Tyler Denniston | 0a145b7 | 2021-01-11 10:51:41 -0500 | [diff] [blame] | 12 | #include "modules/svg/include/SkSVGRenderContext.h" |
Tyler Denniston | df208a3 | 2020-10-30 16:01:54 -0400 | [diff] [blame] | 13 | |
| 14 | sk_sp<SkImageFilter> SkSVGFe::makeImageFilter(const SkSVGRenderContext& ctx, |
Tyler Denniston | b25caae | 2020-11-09 12:46:02 -0500 | [diff] [blame] | 15 | const SkSVGFilterContext& fctx) const { |
| 16 | return this->onMakeImageFilter(ctx, fctx); |
| 17 | } |
| 18 | |
Tyler Denniston | 0a145b7 | 2021-01-11 10:51:41 -0500 | [diff] [blame] | 19 | SkRect 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 Denniston | 94730fe | 2021-01-14 15:53:32 -0500 | [diff] [blame^] | 48 | static 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 Denniston | 62a683e | 2020-12-11 11:47:55 -0500 | [diff] [blame] | 57 | SkRect SkSVGFe::resolveFilterSubregion(const SkSVGRenderContext& ctx, |
| 58 | const SkSVGFilterContext& fctx) const { |
Tyler Denniston | 0a145b7 | 2021-01-11 10:51:41 -0500 | [diff] [blame] | 59 | // 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 Denniston | 94730fe | 2021-01-14 15:53:32 -0500 | [diff] [blame^] | 66 | if (inputs.empty() || AnyIsStandardInput(inputs)) { |
Tyler Denniston | 0a145b7 | 2021-01-11 10:51:41 -0500 | [diff] [blame] | 67 | 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 Denniston | 62a683e | 2020-12-11 11:47:55 -0500 | [diff] [blame] | 93 | } |
| 94 | |
Tyler Denniston | 7bb85db | 2021-01-13 12:08:04 -0500 | [diff] [blame] | 95 | SkSVGColorspace SkSVGFe::resolveColorspace(const SkSVGRenderContext& ctx) const { |
Tyler Denniston | 8f78d55 | 2021-01-14 14:23:13 +0000 | [diff] [blame] | 96 | constexpr SkSVGColorspace kDefaultCS = SkSVGColorspace::kSRGB; |
| 97 | const SkSVGColorspace cs = *ctx.presentationContext().fInherited.fColorInterpolationFilters; |
| 98 | return cs == SkSVGColorspace::kAuto ? kDefaultCS : cs; |
Tyler Denniston | 7bb85db | 2021-01-13 12:08:04 -0500 | [diff] [blame] | 99 | } |
| 100 | |
| 101 | void SkSVGFe::applyProperties(SkSVGRenderContext* ctx) const { this->onPrepareToRender(ctx); } |
| 102 | |
Tyler Denniston | b25caae | 2020-11-09 12:46:02 -0500 | [diff] [blame] | 103 | bool SkSVGFe::parseAndSetAttribute(const char* name, const char* value) { |
| 104 | return INHERITED::parseAndSetAttribute(name, value) || |
| 105 | this->setIn(SkSVGAttributeParser::parse<SkSVGFeInputType>("in", name, value)) || |
Tyler Denniston | 62a683e | 2020-12-11 11:47:55 -0500 | [diff] [blame] | 106 | 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 Denniston | b25caae | 2020-11-09 12:46:02 -0500 | [diff] [blame] | 111 | } |
| 112 | |
| 113 | template <> 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 Denniston | df208a3 | 2020-10-30 16:01:54 -0400 | [diff] [blame] | 135 | } |