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