| /* | |
| * Copyright 2024 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "modules/svg/include/SkSVGFeComponentTransfer.h" | |
| #include "include/core/SkColorFilter.h" | |
| #include "include/core/SkImageFilter.h" | |
| #include "include/core/SkRect.h" | |
| #include "include/effects/SkImageFilters.h" | |
| #include "include/private/SkAssert.h" | |
| #include "include/private/SkFloatingPoint.h" | |
| #include "include/private/SkTArray.h" | |
| #include "include/private/SkTPin.h" | |
| #include "include/private/SkTo.h" | |
| #include "modules/svg/include/SkSVGAttributeParser.h" | |
| #include "modules/svg/include/SkSVGFilterContext.h" | |
| #include "modules/svg/include/SkSVGTypes.h" | |
| #include <cmath> | |
| #include <cstddef> | |
| #include <cstdint> | |
| #include <tuple> | |
| #include <utility> | |
| class SkSVGRenderContext; | |
| sk_sp<SkImageFilter> SkSVGFeComponentTransfer::onMakeImageFilter( | |
| const SkSVGRenderContext& ctx, | |
| const SkSVGFilterContext& fctx) const { | |
| std::vector<uint8_t> a_tbl, b_tbl, g_tbl, r_tbl; | |
| for (const auto& child : fChildren) { | |
| switch (child->tag()) { | |
| case SkSVGTag::kFeFuncA: | |
| a_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable(); | |
| break; | |
| case SkSVGTag::kFeFuncB: | |
| b_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable(); | |
| break; | |
| case SkSVGTag::kFeFuncG: | |
| g_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable(); | |
| break; | |
| case SkSVGTag::kFeFuncR: | |
| r_tbl = static_cast<const SkSVGFeFunc*>(child.get())->getTable(); | |
| break; | |
| default: | |
| break; | |
| } | |
| } | |
| SkASSERT(a_tbl.empty() || a_tbl.size() == 256); | |
| SkASSERT(b_tbl.empty() || b_tbl.size() == 256); | |
| SkASSERT(g_tbl.empty() || g_tbl.size() == 256); | |
| SkASSERT(r_tbl.empty() || r_tbl.size() == 256); | |
| const SkRect cropRect = this->resolveFilterSubregion(ctx, fctx); | |
| const sk_sp<SkImageFilter> input = fctx.resolveInput(ctx, | |
| this->getIn(), | |
| this->resolveColorspace(ctx, fctx)); | |
| const auto cf = SkColorFilters::TableARGB(a_tbl.empty() ? nullptr : a_tbl.data(), | |
| r_tbl.empty() ? nullptr : r_tbl.data(), | |
| g_tbl.empty() ? nullptr : g_tbl.data(), | |
| b_tbl.empty() ? nullptr : b_tbl.data()); | |
| return SkImageFilters::ColorFilter(std::move(cf), std::move(input), cropRect); | |
| } | |
| std::vector<uint8_t> SkSVGFeFunc::getTable() const { | |
| // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute | |
| const auto make_linear = [this]() -> std::vector<uint8_t> { | |
| std::vector<uint8_t> tbl(256); | |
| const float slope = this->getSlope(), | |
| intercept255 = this->getIntercept() * 255; | |
| for (size_t i = 0; i < 256; ++i) { | |
| tbl[i] = SkTPin<int>(sk_float_round2int(intercept255 + i * slope), 0, 255); | |
| } | |
| return tbl; | |
| }; | |
| const auto make_gamma = [this]() -> std::vector<uint8_t> { | |
| std::vector<uint8_t> tbl(256); | |
| const float exponent = this->getExponent(), | |
| offset = this->getOffset(); | |
| for (size_t i = 0; i < 256; ++i) { | |
| const float component = offset + std::pow(i * (1 / 255.f), exponent); | |
| tbl[i] = SkTPin<int>(sk_float_round2int(component * 255), 0, 255); | |
| } | |
| return tbl; | |
| }; | |
| const auto lerp_from_table_values = [this](auto lerp_func) -> std::vector<uint8_t> { | |
| const auto& vals = this->getTableValues(); | |
| if (vals.size() < 2 || vals.size() > 255) { | |
| return {}; | |
| } | |
| // number of interpolation intervals | |
| const size_t n = vals.size() - 1; | |
| std::vector<uint8_t> tbl(256); | |
| for (size_t k = 0; k < n; ++k) { | |
| // interpolation values | |
| const SkSVGNumberType v0 = SkTPin(vals[k + 0], 0.f, 1.f), | |
| v1 = SkTPin(vals[k + 1], 0.f, 1.f); | |
| // start/end component table indices | |
| const size_t c_start = k * 255 / n, | |
| c_end = (k + 1) * 255 / n; | |
| SkASSERT(c_end <= 255); | |
| for (size_t ci = c_start; ci < c_end; ++ci) { | |
| const float lerp_t = static_cast<float>(ci - c_start) / (c_end - c_start), | |
| component = lerp_func(v0, v1, lerp_t); | |
| SkASSERT(component >= 0 && component <= 1); | |
| tbl[ci] = SkToU8(sk_float_round2int(component * 255)); | |
| } | |
| } | |
| tbl.back() = SkToU8(sk_float_round2int(255 * SkTPin(vals.back(), 0.f, 1.f))); | |
| return tbl; | |
| }; | |
| const auto make_table = [&]() -> std::vector<uint8_t> { | |
| return lerp_from_table_values([](float v0, float v1, float t) { | |
| return v0 + (v1 - v0) * t; | |
| }); | |
| }; | |
| const auto make_discrete = [&]() -> std::vector<uint8_t> { | |
| return lerp_from_table_values([](float v0, float v1, float t) { | |
| return v0; | |
| }); | |
| }; | |
| switch (this->getType()) { | |
| case SkSVGFeFuncType::kIdentity: return {}; | |
| case SkSVGFeFuncType::kTable: return make_table(); | |
| case SkSVGFeFuncType::kDiscrete: return make_discrete(); | |
| case SkSVGFeFuncType::kLinear: return make_linear(); | |
| case SkSVGFeFuncType::kGamma: return make_gamma(); | |
| } | |
| SkUNREACHABLE; | |
| } | |
| bool SkSVGFeFunc::parseAndSetAttribute(const char* name, const char* val) { | |
| return INHERITED::parseAndSetAttribute(name, val) || | |
| this->setAmplitude(SkSVGAttributeParser::parse<SkSVGNumberType>("amplitude", name, val)) || | |
| this->setExponent(SkSVGAttributeParser::parse<SkSVGNumberType>("exponent", name, val)) || | |
| this->setIntercept(SkSVGAttributeParser::parse<SkSVGNumberType>("intercept", name, val)) || | |
| this->setOffset(SkSVGAttributeParser::parse<SkSVGNumberType>("offset", name, val)) || | |
| this->setSlope(SkSVGAttributeParser::parse<SkSVGNumberType>("slope", name, val)) || | |
| this->setTableValues(SkSVGAttributeParser::parse<std::vector<SkSVGNumberType>>("tableValues", | |
| name, val)) || | |
| this->setType(SkSVGAttributeParser::parse<SkSVGFeFuncType>("type", name, val)); | |
| } | |
| template <> | |
| bool SkSVGAttributeParser::parse(SkSVGFeFuncType* type) { | |
| static constexpr std::tuple<const char*, SkSVGFeFuncType> gTypeMap[] = { | |
| { "identity", SkSVGFeFuncType::kIdentity }, | |
| { "table" , SkSVGFeFuncType::kTable }, | |
| { "discrete", SkSVGFeFuncType::kDiscrete }, | |
| { "linear" , SkSVGFeFuncType::kLinear }, | |
| { "gamma" , SkSVGFeFuncType::kGamma }, | |
| }; | |
| return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); | |
| } |