blob: 97c4dfad974382e3e56b4c722e0ef937d51c2e9a [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "skia/ext/skcolorspace_trfn.h"
#include <cmath>
namespace skia {
namespace {
// Solve `y` = `alpha` * `x` for `alpha`. Return false if there is no
// solution, or the solution is 0.
bool GetLinearScale(float x, float y, float& alpha) {
if (x == 0.f && y == 0.f) {
alpha = 0.f;
return true;
}
if (x == 0.f || y == 0.f) {
return false;
}
alpha = y / x;
return true;
}
// Solve `y` = pow(`alpha`, 1/`g`) * `x` for `alpha`. Return false if
// there is no solution, or the solution is 0.
bool GetPowerScale(float x, float y, float g, float& alpha) {
if (x == 0.f && y == 0.f) {
alpha = 0.f;
return true;
}
if (x == 0.f || y == 0.f) {
return false;
}
float alpha_to_ginv = y / x;
alpha = std::pow(alpha_to_ginv, g);
return true;
}
} // namespace
skcms_TransferFunction ScaleTransferFunction(const skcms_TransferFunction& f,
float alpha) {
float alpha_to_ginv = std::pow(alpha, 1 / f.g);
return {
f.g, alpha_to_ginv * f.a, alpha_to_ginv * f.b, alpha * f.c,
f.d, alpha * f.e, alpha * f.f,
};
}
bool IsScaledTransferFunction(const skcms_TransferFunction& x,
const skcms_TransferFunction& y,
float* out_alpha) {
// The g and d parameters are unaffected by a scale. Require that they be
// exactly the same.
if (x.g != y.g) {
return false;
}
if (x.d != y.d) {
return false;
}
// Compute alpha for all all variables. If alpha is 0 then the unscaled
// parameter was 0 (and so alpha cannot be computed from it).
float alphas[5] = {0.f, 0.f, 0.f, 0.f, 0.f};
if (!GetLinearScale(x.c, y.c, alphas[0])) {
return false;
}
if (!GetLinearScale(x.e, y.e, alphas[1])) {
return false;
}
if (!GetLinearScale(x.f, y.f, alphas[2])) {
return false;
}
if (!GetPowerScale(x.a, y.a, x.g, alphas[3])) {
return false;
}
if (!GetPowerScale(x.b, y.b, x.g, alphas[4])) {
return false;
}
// Ensure all non-zero alphas are close to each other.
constexpr float kEpsilon = 1e-5f;
float alpha = 0.f;
for (size_t i = 0; i < 5; ++i) {
if (alphas[i] == 0.f) {
continue;
}
if (alpha == 0.f) {
alpha = alphas[i];
continue;
}
if (std::abs(alpha - alphas[i]) > kEpsilon) {
return false;
}
}
// Scaling by zero will just cause bugs, so reject it.
if (alpha == 0.f) {
return false;
}
// Return the result.
if (out_alpha) {
*out_alpha = alpha;
}
return true;
}
} // namespace skia