chromium/third_party/skia/modules/skottie/src/effects/BrightnessContrastEffect.cpp

/*
 * 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