Roll skia/third_party/skcms 5c593bf702db..a52db47aa53f (1 commits)

https://skia.googlesource.com/skcms.git/+log/5c593bf702db..a52db47aa53f

git log 5c593bf702db..a52db47aa53f --date=short --no-merges --format='%ad %ae %s'
2019-10-08 mtklein@google.com sketch PQ and HLG APIs

Created with:
  gclient setdep -r skia/third_party/skcms@a52db47aa53f

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/skcms-skia-autoroll
Please CC kjlubick@google.com,mtklein@google.com on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md

CQ_INCLUDE_TRYBOTS=luci.chromium.try:linux-blink-rel

Bug: None
Change-Id: I9f9eeabe7760c31b73a2ed4d64bce1027fd2a9f7
TBR=kjlubick@google.com,mtklein@google.com
TBR=kjlubick@google.com,mtklein@google.com
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/247074
Reviewed-by: skia-autoroll <skia-autoroll@skia-public.iam.gserviceaccount.com>
Commit-Queue: skia-autoroll <skia-autoroll@skia-public.iam.gserviceaccount.com>
This commit is contained in:
skia-autoroll 2019-10-08 19:22:13 +00:00 committed by Skia Commit-Bot
parent 8803c2d8f3
commit c093cc838f
3 changed files with 119 additions and 28 deletions

View File

@ -51,6 +51,50 @@ SKCMS_API float skcms_TransferFunction_eval (const skcms_TransferFunction*, flo
SKCMS_API bool skcms_TransferFunction_invert(const skcms_TransferFunction*,
skcms_TransferFunction*);
// We can jam a couple alternate transfer function forms into skcms_TransferFunction,
// including those matching the general form of the SMPTE ST 2084 PQ function and its inverse:
//
// max(A + B|x|^C, 0)
// tf(x) = sign(x) * (------------------) ^ F
// (D + E|x|^C)
SKCMS_API bool skcms_TransferFunction_makePQish(skcms_TransferFunction*,
float A, float B, float C,
float D, float E, float F);
static inline bool skcms_TransferFunction_makePQ(skcms_TransferFunction* tf) {
return skcms_TransferFunction_makePQish(tf, -107/128.0f, 1.0f, 32/2523.0f
, 2413/128.0f, -2392/128.0f, 8192/1305.0f);
}
static inline bool skcms_TransferFunction_makePQinv(skcms_TransferFunction* tf) {
return skcms_TransferFunction_makePQish(tf, 107/128.0f, 2413/128.0f, 1305/8192.0f
, 1.0f, 2392/128.0f, 2523/ 32.0f);
}
// skcms_TransferFunction also supports functions of the form of HLG's inverse...
//
// { sign(linear) * ( R|linear|^G ) when 0 <= |linear| <= 1
// encoded = { sign(linear) * ( a ln(|linear|-b) + c ) when 1 < |linear|
SKCMS_API bool skcms_TransferFunction_makeHLGinvish(skcms_TransferFunction*,
float R, float G,
float a, float b, float c);
// ... and of the form of HLG itself, factored to take the same five parameters.
//
// { sign(encoded) * ( (|encoded|/R)^(1/G) ) when 0 <= |encoded| <= R
// linear = { sign(encoded) * ( e^( (|encoded|-c)/a ) + b ) when R < |encoded|
SKCMS_API bool skcms_TransferFunction_makeHLGish(skcms_TransferFunction*,
float R, float G,
float a, float b, float c);
static inline bool skcms_TransferFunction_makeHLG(skcms_TransferFunction* tf) {
return skcms_TransferFunction_makeHLGish(tf,
0.5f, 0.5f , 0.17883277f, 0.28466892f, 0.55991073f);
}
static inline bool skcms_TransferFunction_makeHLGinv(skcms_TransferFunction* tf) {
return skcms_TransferFunction_makeHLGinvish(tf,
0.5f, 0.5f , 0.17883277f, 0.28466892f, 0.55991073f);
}
// Unified representation of 'curv' or 'para' tag data, or a 1D table from 'mft1' or 'mft2'
typedef union skcms_Curve {
struct {

View File

@ -61,6 +61,80 @@ static float minus_1_ulp(float x) {
return x;
}
// Most transfer functions we work with are sRGBish.
// For exotic HDR transfer functions, we encode them using a tf.g that makes no sense,
// and repurpose the other fields to hold the parameters of the HDR functions.
enum TFKind { Bad, sRGBish, PQish, HLGish, HLGinvish };
struct TF_PQish { float A,B,C,D,E,F; };
struct TF_HLGish { float R,G,a,b,c; };
static float TFKind_marker(TFKind kind) {
// We'd use different NaNs, but those aren't guaranteed to be preserved by WASM.
return -(float)kind;
}
static TFKind classify(const skcms_TransferFunction& tf, TF_PQish* pq = nullptr
, TF_HLGish* hlg = nullptr) {
if (tf.g < 0 && (int)tf.g == tf.g) {
// TODO: sanity checks for PQ/HLG like we do for sRGBish.
switch (-(int)tf.g) {
case PQish: if (pq ) { memcpy(pq , &tf.a, sizeof(*pq )); } return PQish;
case HLGish: if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGish;
case HLGinvish: if (hlg) { memcpy(hlg, &tf.a, sizeof(*hlg)); } return HLGinvish;
}
return Bad;
}
// Basic sanity checks for sRGBish transfer functions.
if (isfinitef_(tf.a + tf.b + tf.c + tf.d + tf.e + tf.f + tf.g)
// a,c,d,g should be non-negative to make any sense.
&& tf.a >= 0
&& tf.c >= 0
&& tf.d >= 0
&& tf.g >= 0
// Raising a negative value to a fractional tf->g produces complex numbers.
&& tf.a * tf.d + tf.b >= 0) {
return sRGBish;
}
return Bad;
}
// TODO: temporary shim for old call sites
static bool tf_is_valid(const skcms_TransferFunction* tf) {
return classify(*tf) == sRGBish;
}
bool skcms_TransferFunction_makePQish(skcms_TransferFunction* tf,
float A, float B, float C,
float D, float E, float F) {
*tf = { TFKind_marker(PQish), A,B,C,D,E,F };
assert(classify(*tf) == PQish);
return true;
}
float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) {
float sign = x < 0 ? -1.0f : 1.0f;
x *= sign;
TF_PQish pq;
switch (classify(*tf, &pq)) {
case Bad: break;
case HLGish: break;
case HLGinvish: break;
case sRGBish: return sign * (x < tf->d ? tf->c * x + tf->f
: powf_(tf->a * x + tf->b, tf->g) + tf->e);
case PQish: return sign * powf_(fmaxf_(pq.A + pq.B * powf_(x, pq.C), 0)
/ (pq.D + pq.E * powf_(x, pq.C)),
pq.F);
}
return 0;
}
static float eval_curve(const skcms_Curve* curve, float x) {
if (curve->table_entries == 0) {
return skcms_TransferFunction_eval(&curve->parametric, x);
@ -255,25 +329,6 @@ static bool read_to_XYZD50(const skcms_ICCTag* rXYZ, const skcms_ICCTag* gXYZ,
read_tag_xyz(bXYZ, &toXYZ->vals[0][2], &toXYZ->vals[1][2], &toXYZ->vals[2][2]);
}
static bool tf_is_valid(const skcms_TransferFunction* tf) {
// Reject obviously malformed inputs
if (!isfinitef_(tf->a + tf->b + tf->c + tf->d + tf->e + tf->f + tf->g)) {
return false;
}
// All of these parameters should be non-negative
if (tf->a < 0 || tf->c < 0 || tf->d < 0 || tf->g < 0) {
return false;
}
// It's rather _complex_ to raise a negative number to a fractional power tf->g.
if (tf->a * tf->d + tf->b < 0) {
return false;
}
return true;
}
typedef struct {
uint8_t type [4];
uint8_t reserved_a [4];
@ -1419,14 +1474,6 @@ float powf_(float x, float y) {
: exp2f_(log2f_(x) * y);
}
float skcms_TransferFunction_eval(const skcms_TransferFunction* tf, float x) {
float sign = x < 0 ? -1.0f : 1.0f;
x *= sign;
return sign * (x < tf->d ? tf->c * x + tf->f
: powf_(tf->a * x + tf->b, tf->g) + tf->e);
}
#if defined(__clang__)
[[clang::no_sanitize("float-divide-by-zero")]] // Checked for by tf_is_valid() on the way out.
#endif

View File

@ -1 +1 @@
5c593bf702db07bdd782b58f9bde28e8ee67061b
a52db47aa53f894d1fd478173c7b7c4eb0984f42