/* * Copyright 2020 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "include/core/SkColorFilter.h" #include "include/core/SkData.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/core/SkString.h" #include "include/effects/SkRuntimeEffect.h" #include "include/private/base/SkAssert.h" #include "include/private/base/SkTPin.h" #include "modules/skottie/src/Adapter.h" #include "modules/skottie/src/SkottiePriv.h" #include "modules/skottie/src/SkottieValue.h" #include "modules/skottie/src/effects/Effects.h" #include "modules/sksg/include/SkSGColorFilter.h" #include "modules/sksg/include/SkSGRenderNode.h" #include <algorithm> #include <cmath> #include <cstddef> #include <utility> namespace skjson { class ArrayValue; } namespace skottie::internal { namespace { // The contrast effect transfer function can be approximated with the following // 3rd degree polynomial: // // f(x) = -2πC/3 * x³ + πC * x² + (1 - πC/3) * x // // where C is the normalized contrast value [-1..1]. // // Derivation: // // - start off with sampling the AE contrast effect for various contrast/input values [1] // // - apply cubic polynomial curve fitting to determine best-fit coefficients for given // contrast values [2] // // - observations: // * negative contrast appears clamped at -0.5 (-50) // * a,b coefficients vary linearly vs. contrast // * the b coefficient for max contrast (1.0) looks kinda familiar: 3.14757 - coincidence? // probably not. let's run with it: b == πC // // - additionally, we expect the following to hold: // * f(0 ) = 0 \ | d = 0 // * f(1 ) = 1 | => | a = -2b/3 // * f(0.5) = 0.5 / | c = 1 - b/3 // // - this yields a pretty decent approximation: [3] // // // Note (courtesy of mtklein, reed): [4] seems to yield a closer approximation, but requires // a more expensive sin // // f(x) = x + a * sin(2πx)/2π // // [1] https://www.desmos.com/calculator/oksptqpo8z // [2] https://www.desmos.com/calculator/oukrf6yahn // [3] https://www.desmos.com/calculator/ehem0vy3ft // [4] https://www.desmos.com/calculator/5t4xi10q4v // #ifndef SKOTTIE_ACCURATE_CONTRAST_APPROXIMATION static sk_sp<SkData> make_contrast_coeffs(float contrast) { … } static constexpr char CONTRAST_EFFECT[] = … ; #else // More accurate (but slower) approximation: // // f(x) = x + a * sin(2πx) // // a = -contrast/3π // static sk_sp<SkData> make_contrast_coeffs(float contrast) { const auto coeff_a = -contrast / (3 * SK_ScalarPI); return SkData::MakeWithCopy(&coeff_a, sizeof(coeff_a)); } static constexpr char CONTRAST_EFFECT[] = "uniform half a;" "half4 main(half4 color) {" "color.rgb += a * sin(color.rgb * 6.283185);" "return color;" "}" ; #endif // Brightness transfer function approximation: // // f(x) = 1 - (1 - x)^(2^(1.8*B)) // // where B is the normalized [-1..1] brightness value // // Visualization: https://www.desmos.com/calculator/wuyqa2wtol // static sk_sp<SkData> make_brightness_coeffs(float brightness) { … } static constexpr char BRIGHTNESS_EFFECT[] = … ; class BrightnessContrastAdapter final : public DiscardableAdapterBase<BrightnessContrastAdapter, sksg::ExternalColorFilter> { … }; } // namespace sk_sp<sksg::RenderNode> EffectBuilder::attachBrightnessContrastEffect( const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const { … } } // namespace skottie::internal