support generalized HLG

Same basic deal as skcms.

This new GM tests our treatment of color spaces as sources is consistent
with our treatment of color spaces as destinations.  It looks good to me
now, and I have tested that this GM catches a "well-placed typo" in each
of the three implementations modified here.

Bug: chromium:1144260
Change-Id: I3eabc93bbd65855c60006751f68c171ccdce9d94
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/351336
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Mike Klein <mtklein@google.com>
This commit is contained in:
Mike Klein 2021-01-07 10:50:01 -06:00 committed by Skia Commit-Bot
parent 8194c3024f
commit 627c0022ff
6 changed files with 104 additions and 11 deletions

View File

@ -1328,8 +1328,8 @@ struct Task {
case HLGish_TF:
if (eq(tf, SkNamedTransferFn::kHLG)) { return SkString("HLG"); }
return SkStringPrintf("HLGish %.3g %.3g %.3g %.3g %.3g",
tf.a, tf.b, tf.c, tf.d, tf.e);
return SkStringPrintf("HLGish %.3g %.3g %.3g %.3g %.3g (%.3g)",
tf.a, tf.b, tf.c, tf.d, tf.e, tf.f+1);
case HLGinvish_TF: break;
case Bad_TF: break;

86
gm/colorspace.cpp Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright 2021 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "tools/Resources.h"
static const skcms_TransferFunction gTFs[] = {
SkNamedTransferFn::kSRGB,
SkNamedTransferFn::k2Dot2,
SkNamedTransferFn::kLinear,
SkNamedTransferFn::kRec2020,
SkNamedTransferFn::kPQ,
SkNamedTransferFn::kHLG,
{-3.0f, 2.0f, 2.0f, 1/0.17883277f, 0.28466892f, 0.55991073f, 3.0f }, // HLG scaled 4x
};
static const skcms_Matrix3x3 gGamuts[] = {
SkNamedGamut::kSRGB,
SkNamedGamut::kAdobeRGB,
SkNamedGamut::kDisplayP3,
SkNamedGamut::kRec2020,
SkNamedGamut::kXYZ,
};
static const int W = 128,
H = 128;
// This GM demonstrates that our color space management is self-consistent.
// (Important to note, self-consistent, not necessarily correct in an objective sense.)
//
// Let's let,
//
// SkColorSpace* imgCS = img->colorSpace();
// SkColorSpace* dstCS = canvas->imageInfo().colorSpace();
//
// Ordinarily we'd just
//
// canvas->drawImage(img, 0,0);
//
// which would convert that img's pixels from imgCS to dstCS while drawing.
//
// But before we draw in this GM we convert the image to an arbitrarily different color space,
// letting midCS range over the cross-product gTFs × gGamuts:
//
// canvas->drawImage(img->makeColorSpace(midCS), 0,0);
//
// This converts img first from imgCS to midCS, treating midCS as a destination color space,
// and then draws that midCS image to the dstCS canvas, treating midCS as a source color space.
//
// This should draw a grid of images that look identical except for small precision loss.
DEF_SIMPLE_GM(colorspace, canvas, W*SK_ARRAY_COUNT(gTFs), H*SK_ARRAY_COUNT(gGamuts)) {
if (!canvas->imageInfo().colorSpace()) {
canvas->drawString("This GM only makes sense with color-managed drawing.",
W,H, SkFont{}, SkPaint{});
return;
}
sk_sp<SkImage> img = GetResourceAsImage("images/mandrill_128.png");
if (!img) {
canvas->drawString("Could not load our test image!",
W,H, SkFont{}, SkPaint{});
return;
}
SkASSERT(img->width() == W);
SkASSERT(img->height() == H);
SkASSERT(img->colorSpace());
for (skcms_Matrix3x3 gamut : gGamuts) {
canvas->save();
for (skcms_TransferFunction tf : gTFs) {
sk_sp<SkColorSpace> midCS = SkColorSpace::MakeRGB(tf, gamut);
canvas->drawImage(img->makeColorSpace(midCS), 0,0);
canvas->translate(W, 0);
}
canvas->restore();
canvas->translate(0, H);
}
}

View File

@ -90,6 +90,7 @@ gm_sources = [
"$_gm/colorfilterimagefilter.cpp",
"$_gm/colorfilters.cpp",
"$_gm/colormatrix.cpp",
"$_gm/colorspace.cpp",
"$_gm/colorwheel.cpp",
"$_gm/complexclip.cpp",
"$_gm/complexclip2.cpp",

View File

@ -161,17 +161,20 @@ skvm::Color sk_program_transfer_fn(skvm::Builder* p, skvm::Uniforms* uniforms,
break;
case PQish_TF: {
auto vC = approx_powf(v, C);
skvm::F32 vC = approx_powf(v, C);
v = approx_powf(max(B * vC + A, 0.0f) / (E * vC + D), F);
} break;
case HLGish_TF: {
auto vA = v * A;
v = select(vA <= 1.0f, approx_powf(vA, B)
, approx_exp((v-E) * C + D));
skvm::F32 vA = v*A,
K = F + 1.0f;
v = K*select(vA <= 1.0f, approx_powf(vA, B)
, approx_exp((v-E) * C + D));
} break;
case HLGinvish_TF:
skvm::F32 K = F + 1.0f;
v /= K;
v = select(v <= 1.0f, A * approx_powf(v, B)
, C * approx_log(v-D) + E);
break;

View File

@ -175,10 +175,10 @@ void GrGLSLShaderBuilder::appendColorGamutXform(SkString* out,
body.append("x = pow(max(A + B * pow(x, C), 0) / (D + E * pow(x, C)), F);");
break;
case TFKind::HLGish_TF:
body.append("x = (x*A <= 1) ? pow(x*A, B) : exp((x-E)*C) + D;");
body.append("x = (x*A <= 1) ? pow(x*A, B) : exp((x-E)*C) + D; x *= (F+1);");
break;
case TFKind::HLGinvish_TF:
body.append("x = (x <= 1) ? A * pow(x, B) : C * log(x - D) + E;");
body.append("x /= (F+1); x = (x <= 1) ? A * pow(x, B) : C * log(x - D) + E;");
break;
default:
SkASSERT(false);

View File

@ -1877,12 +1877,13 @@ STAGE(HLGish, const skcms_TransferFunction* ctx) {
v = strip_sign(v, &sign);
const float R = ctx->a, G = ctx->b,
a = ctx->c, b = ctx->d, c = ctx->e;
a = ctx->c, b = ctx->d, c = ctx->e,
K = ctx->f + 1.0f;
F r = if_then_else(v*R <= 1, approx_powf(v*R, G)
, approx_exp((v-c)*a) + b);
return apply_sign(r, sign);
return K * apply_sign(r, sign);
};
r = fn(r);
g = fn(g);
@ -1895,8 +1896,10 @@ STAGE(HLGinvish, const skcms_TransferFunction* ctx) {
v = strip_sign(v, &sign);
const float R = ctx->a, G = ctx->b,
a = ctx->c, b = ctx->d, c = ctx->e;
a = ctx->c, b = ctx->d, c = ctx->e,
K = ctx->f + 1.0f;
v /= K;
F r = if_then_else(v <= 1, R * approx_powf(v, G)
, a * approx_log(v - b) + c);