xref: /aosp_15_r20/external/skia/modules/svg/src/SkSVGFeComponentTransfer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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