1 /*
2 * Copyright 2024 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/SkSVGFeComponentTransfer.h"
9
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkImageFilter.h"
12 #include "include/core/SkRect.h"
13 #include "include/effects/SkImageFilters.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkFloatingPoint.h"
16 #include "include/private/base/SkTArray.h"
17 #include "include/private/base/SkTPin.h"
18 #include "include/private/base/SkTo.h"
19 #include "modules/svg/include/SkSVGAttributeParser.h"
20 #include "modules/svg/include/SkSVGFilterContext.h"
21 #include "modules/svg/include/SkSVGTypes.h"
22
23 #include <cmath>
24 #include <cstddef>
25 #include <cstdint>
26 #include <tuple>
27 #include <utility>
28
29 class SkSVGRenderContext;
30
onMakeImageFilter(const SkSVGRenderContext & ctx,const SkSVGFilterContext & fctx) const31 sk_sp<SkImageFilter> SkSVGFeComponentTransfer::onMakeImageFilter(
32 const SkSVGRenderContext& ctx,
33 const SkSVGFilterContext& fctx) const {
34 std::vector<uint8_t> a_tbl, b_tbl, g_tbl, r_tbl;
35
36 for (const auto& child : fChildren) {
37 switch (child->tag()) {
38 case SkSVGTag::kFeFuncA:
39 a_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable();
40 break;
41 case SkSVGTag::kFeFuncB:
42 b_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable();
43 break;
44 case SkSVGTag::kFeFuncG:
45 g_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable();
46 break;
47 case SkSVGTag::kFeFuncR:
48 r_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable();
49 break;
50 default:
51 break;
52 }
53 }
54 SkASSERT(a_tbl.empty() || a_tbl.size() == 256);
55 SkASSERT(b_tbl.empty() || b_tbl.size() == 256);
56 SkASSERT(g_tbl.empty() || g_tbl.size() == 256);
57 SkASSERT(r_tbl.empty() || r_tbl.size() == 256);
58
59 const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx);
60 const sk_sp<SkImageFilter> input = fctx.resolveInput(ctx,
61 this->getIn(),
62 this->resolveColorspace(ctx, fctx));
63
64 const auto cf = SkColorFilters::TableARGB(a_tbl.empty() ? nullptr : a_tbl.data(),
65 r_tbl.empty() ? nullptr : r_tbl.data(),
66 g_tbl.empty() ? nullptr : g_tbl.data(),
67 b_tbl.empty() ? nullptr : b_tbl.data());
68
69 return SkImageFilters::ColorFilter(std::move(cf), std::move(input), cropRect);
70 }
71
getTable() const72 std::vector<uint8_t> SkSVGFeFunc::getTable() const {
73 // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute
74 const auto make_linear = [this]() -> std::vector<uint8_t> {
75 std::vector<uint8_t> tbl(256);
76 const float slope = this->getSlope(),
77 intercept255 = this->getIntercept() * 255;
78
79 for (size_t i = 0; i < 256; ++i) {
80 tbl[i] = SkTPin<int>(sk_float_round2int(intercept255 + i * slope), 0, 255);
81 }
82
83 return tbl;
84 };
85
86 const auto make_gamma = [this]() -> std::vector<uint8_t> {
87 std::vector<uint8_t> tbl(256);
88 const float exponent = this->getExponent(),
89 offset = this->getOffset();
90
91 for (size_t i = 0; i < 256; ++i) {
92 const float component = offset + std::pow(i * (1 / 255.f), exponent);
93 tbl[i] = SkTPin<int>(sk_float_round2int(component * 255), 0, 255);
94 }
95
96 return tbl;
97 };
98
99 const auto lerp_from_table_values = [this](auto lerp_func) -> std::vector<uint8_t> {
100 const auto& vals = this->getTableValues();
101 if (vals.size() < 2 || vals.size() > 255) {
102 return {};
103 }
104
105 // number of interpolation intervals
106 const size_t n = vals.size() - 1;
107
108 std::vector<uint8_t> tbl(256);
109 for (size_t k = 0; k < n; ++k) {
110 // interpolation values
111 const SkSVGNumberType v0 = SkTPin(vals[k + 0], 0.f, 1.f),
112 v1 = SkTPin(vals[k + 1], 0.f, 1.f);
113
114 // start/end component table indices
115 const size_t c_start = k * 255 / n,
116 c_end = (k + 1) * 255 / n;
117 SkASSERT(c_end <= 255);
118
119 for (size_t ci = c_start; ci < c_end; ++ci) {
120 const float lerp_t = static_cast<float>(ci - c_start) / (c_end - c_start),
121 component = lerp_func(v0, v1, lerp_t);
122 SkASSERT(component >= 0 && component <= 1);
123
124 tbl[ci] = SkToU8(sk_float_round2int(component * 255));
125 }
126 }
127
128 tbl.back() = SkToU8(sk_float_round2int(255 * SkTPin(vals.back(), 0.f, 1.f)));
129
130 return tbl;
131 };
132
133 const auto make_table = [&]() -> std::vector<uint8_t> {
134 return lerp_from_table_values([](float v0, float v1, float t) {
135 return v0 + (v1 - v0) * t;
136 });
137 };
138
139 const auto make_discrete = [&]() -> std::vector<uint8_t> {
140 return lerp_from_table_values([](float v0, float v1, float t) {
141 return v0;
142 });
143 };
144
145 switch (this->getType()) {
146 case SkSVGFeFuncType::kIdentity: return {};
147 case SkSVGFeFuncType::kTable: return make_table();
148 case SkSVGFeFuncType::kDiscrete: return make_discrete();
149 case SkSVGFeFuncType::kLinear: return make_linear();
150 case SkSVGFeFuncType::kGamma: return make_gamma();
151 }
152
153 SkUNREACHABLE;
154 }
155
parseAndSetAttribute(const char * name,const char * val)156 bool SkSVGFeFunc::parseAndSetAttribute(const char* name, const char* val) {
157 return INHERITED::parseAndSetAttribute(name, val) ||
158 this->setAmplitude(SkSVGAttributeParser::parse<SkSVGNumberType>("amplitude", name, val)) ||
159 this->setExponent(SkSVGAttributeParser::parse<SkSVGNumberType>("exponent", name, val)) ||
160 this->setIntercept(SkSVGAttributeParser::parse<SkSVGNumberType>("intercept", name, val)) ||
161 this->setOffset(SkSVGAttributeParser::parse<SkSVGNumberType>("offset", name, val)) ||
162 this->setSlope(SkSVGAttributeParser::parse<SkSVGNumberType>("slope", name, val)) ||
163 this->setTableValues(SkSVGAttributeParser::parse<std::vector<SkSVGNumberType>>("tableValues",
164 name, val)) ||
165 this->setType(SkSVGAttributeParser::parse<SkSVGFeFuncType>("type", name, val));
166 }
167
168 template <>
parse(SkSVGFeFuncType * type)169 bool SkSVGAttributeParser::parse(SkSVGFeFuncType* type) {
170 static constexpr std::tuple<const char*, SkSVGFeFuncType> gTypeMap[] = {
171 { "identity", SkSVGFeFuncType::kIdentity },
172 { "table" , SkSVGFeFuncType::kTable },
173 { "discrete", SkSVGFeFuncType::kDiscrete },
174 { "linear" , SkSVGFeFuncType::kLinear },
175 { "gamma" , SkSVGFeFuncType::kGamma },
176 };
177
178 return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken();
179 }
180