/* * Copyright 2024 Google Inc. * * 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/base/SkAssert.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkTArray.h" #include "include/private/base/SkTPin.h" #include "include/private/base/SkTo.h" #include "modules/svg/include/SkSVGAttributeParser.h" #include "modules/svg/include/SkSVGFilterContext.h" #include "modules/svg/include/SkSVGTypes.h" #include #include #include #include #include class SkSVGRenderContext; sk_sp SkSVGFeComponentTransfer::onMakeImageFilter( const SkSVGRenderContext& ctx, const SkSVGFilterContext& fctx) const { std::vector a_tbl, b_tbl, g_tbl, r_tbl; for (const auto& child : fChildren) { switch (child->tag()) { case SkSVGTag::kFeFuncA: a_tbl = static_cast(child.get())->getTable(); break; case SkSVGTag::kFeFuncB: b_tbl = static_cast(child.get())->getTable(); break; case SkSVGTag::kFeFuncG: g_tbl = static_cast(child.get())->getTable(); break; case SkSVGTag::kFeFuncR: r_tbl = static_cast(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 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 SkSVGFeFunc::getTable() const { // https://www.w3.org/TR/SVG11/filters.html#feComponentTransferTypeAttribute const auto make_linear = [this]() -> std::vector { std::vector tbl(256); const float slope = this->getSlope(), intercept255 = this->getIntercept() * 255; for (size_t i = 0; i < 256; ++i) { tbl[i] = SkTPin(sk_float_round2int(intercept255 + i * slope), 0, 255); } return tbl; }; const auto make_gamma = [this]() -> std::vector { std::vector 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(sk_float_round2int(component * 255), 0, 255); } return tbl; }; const auto lerp_from_table_values = [this](auto lerp_func) -> std::vector { 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 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(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 { return lerp_from_table_values([](float v0, float v1, float t) { return v0 + (v1 - v0) * t; }); }; const auto make_discrete = [&]() -> std::vector { 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("amplitude", name, val)) || this->setExponent(SkSVGAttributeParser::parse("exponent", name, val)) || this->setIntercept(SkSVGAttributeParser::parse("intercept", name, val)) || this->setOffset(SkSVGAttributeParser::parse("offset", name, val)) || this->setSlope(SkSVGAttributeParser::parse("slope", name, val)) || this->setTableValues(SkSVGAttributeParser::parse>("tableValues", name, val)) || this->setType(SkSVGAttributeParser::parse("type", name, val)); } template <> bool SkSVGAttributeParser::parse(SkSVGFeFuncType* type) { static constexpr std::tuple gTypeMap[] = { { "identity", SkSVGFeFuncType::kIdentity }, { "table" , SkSVGFeFuncType::kTable }, { "discrete", SkSVGFeFuncType::kDiscrete }, { "linear" , SkSVGFeFuncType::kLinear }, { "gamma" , SkSVGFeFuncType::kGamma }, }; return this->parseEnumMap(gTypeMap, type) && this->parseEOSToken(); }