diff --git a/gdk/gdkcolordefs.h b/gdk/gdkcolordefs.h index d1dc8dd86b..25394cac55 100644 --- a/gdk/gdkcolordefs.h +++ b/gdk/gdkcolordefs.h @@ -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 diff --git a/gdk/gdkcolorstate.c b/gdk/gdkcolorstate.c index 7993d3de4c..f6493de173 100644 --- a/gdk/gdkcolorstate.c +++ b/gdk/gdkcolorstate.c @@ -425,15 +425,15 @@ struct _GdkCicpColorState GdkColorState *no_srgb; - const char *name; + char *name; GdkTransferFunc eotf; GdkTransferFunc oetf; - float *to_srgb; - float *to_rec2020; - float *from_srgb; - float *from_rec2020; + float to_srgb[9]; + float to_rec2020[9]; + float from_srgb[9]; + float from_rec2020[9]; GdkCicp cicp; }; @@ -461,14 +461,11 @@ gdk_cicp_color_state_free (GdkColorState *cs) { GdkCicpColorState *self = (GdkCicpColorState *) cs; + g_free (self->name); + if (self->no_srgb) gdk_color_state_unref (self->no_srgb); - g_free (self->to_srgb); - g_free (self->to_rec2020); - g_free (self->from_srgb); - g_free (self->from_rec2020); - g_free (self); } @@ -558,7 +555,7 @@ gdk_cicp_color_state_get_cicp (GdkColorState *color_state) return &self->cicp; } -/* }}} */ +/* }}} */ static const GdkColorStateClass GDK_CICP_COLOR_STATE_CLASS = { @@ -707,10 +704,10 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp, self->eotf = eotf; self->oetf = oetf; - self->to_srgb = multiply (g_new (float, 9), xyz_to_srgb, to_xyz); - self->to_rec2020 = multiply (g_new (float, 9), xyz_to_rec2020, to_xyz); - self->from_srgb = multiply (g_new (float, 9), from_xyz, srgb_to_xyz); - self->from_rec2020 = multiply (g_new (float, 9), from_xyz, rec2020_to_xyz); + multiply (self->to_srgb, xyz_to_srgb, to_xyz); + multiply (self->to_rec2020, xyz_to_rec2020, to_xyz); + multiply (self->from_srgb, from_xyz, srgb_to_xyz); + multiply (self->from_rec2020, from_xyz, rec2020_to_xyz); self->name = g_strdup_printf ("cicp-%u/%u/%u/%u", cicp->color_primaries, @@ -720,12 +717,12 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp, if (cicp->transfer_function == 13) { - GdkCicp no_srgb; - - memcpy (&no_srgb, cicp, sizeof (GdkCicp)); - no_srgb.transfer_function = 8; - - self->no_srgb = gdk_color_state_new_for_cicp (&no_srgb, NULL); + self->no_srgb = gdk_color_state_new_for_cicp (&(GdkCicp) { + cicp->color_primaries, + 8, + cicp->matrix_coefficients, + cicp->range }, + NULL); } return (GdkColorState *) self; diff --git a/gsk/gpu/shaders/color.glsl b/gsk/gpu/shaders/color.glsl index 979d409da7..4364392b13 100644 --- a/gsk/gpu/shaders/color.glsl +++ b/gsk/gpu/shaders/color.glsl @@ -63,7 +63,7 @@ float srgb_oetf (float v) { if (abs (v) > 0.0031308) - return 1.055 * sign (v) * pow (abs (v), 1.0 / 2.4) - 0.055; + return sign (v) * (1.055 * pow (abs (v), 1.0 / 2.4) - 0.055); else return 12.92 * v; } diff --git a/gsk/gpu/shaders/gskgpuconvertcicp.glsl b/gsk/gpu/shaders/gskgpuconvertcicp.glsl index da03534221..35045564f0 100644 --- a/gsk/gpu/shaders/gskgpuconvertcicp.glsl +++ b/gsk/gpu/shaders/gskgpuconvertcicp.glsl @@ -173,7 +173,7 @@ bt709_oetf (float v) if (abs (v) < 0.081) return v * 4.5; else - return 1.099 * sign (v) * pow (abs (v), 0.45) - 0.099; + return 1.099 * sign (v) * (pow (abs (v), 0.45) - 0.099); } float @@ -207,10 +207,10 @@ hlg_eotf (float v) const float b = 0.28466892; const float c = 0.55991073; - if (v <= 0.5) - return (v * v) / 3.0; + if (abs (v) <= 0.5) + return sign (v) * ((v * v) / 3.0); else - return exp (((v - c) / a) + b) / 12.0; + return sign (v) * (exp (((abs (v) - c) / a) + b) / 12.0); } float @@ -220,10 +220,10 @@ hlg_oetf (float v) const float b = 0.28466892; const float c = 0.55991073; - if (v <= 1.0 / 12.0) - return sqrt (3.0 * v); + if (abs (v) <= 1.0 / 12.0) + return sign (v) * sqrt (3.0 * abs (v)); else - return a * log (12.0 * v - b) + c; + return sign (v) * (a * log (12.0 * abs (v) - b) + c); } vec3 diff --git a/testsuite/gdk/colorstate-internal.c b/testsuite/gdk/colorstate-internal.c index a95dd87577..9c90fc6915 100644 --- a/testsuite/gdk/colorstate-internal.c +++ b/testsuite/gdk/colorstate-internal.c @@ -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];