mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-09 10:20:07 +00:00
Fix up transfer function symmetry
The easiest things trigger the silliest mistakes. Add tests for various properties we want our transfer functions to have, such as: - be inverse of each other - stay within the defined ranges - by symmetric around 0
This commit is contained in:
parent
0f85a40a25
commit
f62606105e
@ -30,7 +30,7 @@ static inline float
|
||||
srgb_oetf (float v)
|
||||
{
|
||||
if (fabsf (v) > 0.0031308f)
|
||||
return 1.055f * sign (v) * powf (fabsf (v), 1.f / 2.4f) - 0.055f;
|
||||
return sign (v) * (1.055f * powf (fabsf (v), 1.f / 2.4f) - 0.055f);
|
||||
else
|
||||
return 12.92f * v;
|
||||
}
|
||||
@ -118,7 +118,7 @@ bt709_oetf (float v)
|
||||
if (fabsf (v) < b)
|
||||
return v * 4.5f;
|
||||
else
|
||||
return a * sign (v) * powf (fabsf (v), 0.45f) - (a - 1);
|
||||
return sign (v) * (a * powf (fabsf (v), 0.45f) - (a - 1));
|
||||
}
|
||||
|
||||
static inline float
|
||||
@ -128,10 +128,10 @@ hlg_eotf (float v)
|
||||
const float b = 0.28466892;
|
||||
const float c = 0.55991073;
|
||||
|
||||
if (v <= 0.5)
|
||||
return (v * v) / 3;
|
||||
if (fabsf (v) <= 0.5)
|
||||
return sign (v) * (v * v) / 3;
|
||||
else
|
||||
return (expf ((v - c) / a) + b) / 12.0;
|
||||
return sign (v) * (expf ((fabsf (v) - c) / a) + b) / 12.0;
|
||||
}
|
||||
|
||||
static inline float
|
||||
@ -141,10 +141,10 @@ hlg_oetf (float v)
|
||||
const float b = 0.28466892;
|
||||
const float c = 0.55991073;
|
||||
|
||||
if (v <= 1/12.0)
|
||||
return sqrtf (3 * v);
|
||||
if (fabsf (v) <= 1/12.0)
|
||||
return sign (v) * sqrtf (3 * fabsf (v));
|
||||
else
|
||||
return a * logf (12 * v - b) + c;
|
||||
return sign (v) * (a * logf (12 * fabsf (v) - b) + c);
|
||||
}
|
||||
|
||||
/* See http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
|
||||
|
@ -12,30 +12,84 @@ typedef struct
|
||||
const char *name;
|
||||
TransferFunc oetf;
|
||||
TransferFunc eotf;
|
||||
float o_range[2];
|
||||
float e_range[2];
|
||||
} TransferTest;
|
||||
|
||||
TransferTest transfers[] = {
|
||||
{ "srgb", srgb_oetf, srgb_eotf },
|
||||
{ "pq", pq_oetf, pq_eotf },
|
||||
{ "bt709", bt709_oetf, bt709_eotf },
|
||||
{ "hlg", hlg_oetf, hlg_eotf },
|
||||
{ "gamma22", gamma22_oetf, gamma22_eotf },
|
||||
{ "gamma28", gamma28_oetf, gamma28_eotf },
|
||||
{ "srgb", srgb_oetf, srgb_eotf, { 0, 1 }, { 0, 1} },
|
||||
{ "pq", pq_oetf, pq_eotf, { 0, 49.2610855 }, { 0, 1 } },
|
||||
{ "bt709", bt709_oetf, bt709_eotf, { 0, 1 }, { 0, 1 } },
|
||||
{ "hlg", hlg_oetf, hlg_eotf, { 0, 1}, { 0, 1} },
|
||||
{ "gamma22", gamma22_oetf, gamma22_eotf, { 0, 1 }, { 0, 1 } },
|
||||
{ "gamma28", gamma28_oetf, gamma28_eotf, { 0, 1 }, { 0, 1 } },
|
||||
};
|
||||
|
||||
#define LERP(t, a, b) ((a) + (t) * ((b) - (a)))
|
||||
|
||||
#define ASSERT_IN_RANGE(v, a, b, epsilon) \
|
||||
g_assert_cmpfloat_with_epsilon (MIN(v,a), a, epsilon); \
|
||||
g_assert_cmpfloat_with_epsilon (MAX(v,b), b, epsilon); \
|
||||
|
||||
static void
|
||||
test_transfer (gconstpointer data)
|
||||
{
|
||||
TransferTest *transfer = (TransferTest *) data;
|
||||
float v, v1, v2;
|
||||
|
||||
for (int i = 0; i < 1000; i++)
|
||||
for (int i = 0; i < 1001; i++)
|
||||
{
|
||||
float v = i / 1000.0;
|
||||
float v2 = transfer->oetf (transfer->eotf (v));
|
||||
v = LERP (i/1000.0, transfer->e_range[0], transfer->e_range[1]);
|
||||
|
||||
v1 = transfer->eotf (v);
|
||||
|
||||
ASSERT_IN_RANGE (v1, transfer->o_range[0], transfer->o_range[1], 0.0001);
|
||||
|
||||
v2 = transfer->oetf (v1);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (v, v2, 0.05);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 1001; i++)
|
||||
{
|
||||
v = LERP (i/1000.0, transfer->o_range[0], transfer->o_range[1]);
|
||||
|
||||
v1 = transfer->oetf (v);
|
||||
|
||||
ASSERT_IN_RANGE (v1, transfer->e_range[0], transfer->e_range[1], 0.0001);
|
||||
|
||||
v2 = transfer->eotf (v1);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (v, v2, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_transfer_symmetry (gconstpointer data)
|
||||
{
|
||||
TransferTest *transfer = (TransferTest *) data;
|
||||
float v, v1, v2;
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
v = LERP (i/10.0, transfer->e_range[0], transfer->e_range[1]);
|
||||
|
||||
v1 = transfer->eotf (v);
|
||||
v2 = -transfer->eotf (-v);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (v1, v2, 0.05);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
{
|
||||
v = LERP (i/10.0, transfer->o_range[0], transfer->o_range[1]);
|
||||
|
||||
v1 = transfer->oetf (v);
|
||||
v2 = -transfer->oetf (-v);
|
||||
|
||||
g_assert_cmpfloat_with_epsilon (v1, v2, 0.05);
|
||||
}
|
||||
}
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
@ -171,6 +225,14 @@ main (int argc, char *argv[])
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (transfers); i++)
|
||||
{
|
||||
TransferTest *test = &transfers[i];
|
||||
char *path = g_strdup_printf ("/colorstate/transfer-symmetry/%s", test->name);
|
||||
g_test_add_data_func (path, test, test_transfer_symmetry);
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
for (guint i = 0; i < G_N_ELEMENTS (matrices); i++)
|
||||
{
|
||||
MatrixTest *test = &matrices[i];
|
||||
|
Loading…
Reference in New Issue
Block a user