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