From c023762329cd3775a62f6715dab341f6b94c62f2 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 21 Aug 2024 09:08:16 -0400 Subject: [PATCH] wip: Hack tone mapping into gtk4-image-tool This is just for playing around, not intended to be merged. --- gdk/gdkhdrmetadataprivate.h | 12 ++ tools/gtk-image-tool-convert.c | 306 ++++++++++++++++++++++++++++++++- tools/meson.build | 2 +- 3 files changed, 313 insertions(+), 7 deletions(-) diff --git a/gdk/gdkhdrmetadataprivate.h b/gdk/gdkhdrmetadataprivate.h index 4745b444db..285a63332f 100644 --- a/gdk/gdkhdrmetadataprivate.h +++ b/gdk/gdkhdrmetadataprivate.h @@ -16,3 +16,15 @@ struct _GdkHdrMetadata float max_cll; float max_fall; }; + +static inline char * +gdk_hdr_metadata_to_string (GdkHdrMetadata *hdr) +{ + return g_strdup_printf ("r %f %f, g %f %f, b %f %f, w %f %f, lum %f %f, avg %f %f", + hdr->rx, hdr->ry, + hdr->gx, hdr->gy, + hdr->bx, hdr->by, + hdr->wx, hdr->wy, + hdr->min_lum, hdr->max_lum, + hdr->max_cll, hdr->max_fall); +} diff --git a/tools/gtk-image-tool-convert.c b/tools/gtk-image-tool-convert.c index 79e33050ff..cf324e1cda 100644 --- a/tools/gtk-image-tool-convert.c +++ b/tools/gtk-image-tool-convert.c @@ -29,14 +29,190 @@ #include #include "gtk-image-tool.h" +// TEMPORARY + +#include "gdk/gdkcolordefs.h" +#include "gdk/gdkcolorprivate.h" +#include "gdk/gdkhdrmetadataprivate.h" + +#undef gdk_color_state_ref +#undef gdk_color_state_unref + +static void +multiply (const float m[9], + const float v[3], + float res[3]) +{ + for (int i = 0; i < 3; i++) + res[i] = m[3*i+0]*v[0] + m[3*i+1]*v[1] + m[3*i+2]*v[2]; +} + +static const float rec2020_to_lms[9] = { + 0.412109, 0.523926, 0.063965, + 0.166748, 0.720459, 0.112793, + 0.024170, 0.075439, 0.900391, +}; + +static const float lms_to_ictcp[9] = { + 0.500000, 0.500000, 0.000000, + 1.613770, -3.323486, 1.709717, + 4.378174, -4.245605, -0.132568, +}; + +static const float lms_to_rec2020[9] = { + 3.436607, -2.506452, 0.069845, + -0.791330, 1.983600, -0.192271, + -0.025950, -0.098914, 1.124864, +}; + +static const float ictcp_to_lms[9] = { + 1.000000, 0.008609, 0.111030, + 1.000000, -0.008609, -0.111030, + 1.000000, 0.560031, -0.320627, +}; + +static void +rec2100_linear_to_ictcp (float in[4], + float out[4]) +{ + float lms[3]; + + multiply (rec2020_to_lms, in, lms); + + lms[0] = pq_oetf (lms[0]); + lms[1] = pq_oetf (lms[1]); + lms[2] = pq_oetf (lms[2]); + + multiply (lms_to_ictcp, lms, out); + + out[3] = in[3]; +} + +static void +ictcp_to_rec2100_linear (float in[4], + float out[4]) +{ + float lms[3]; + + multiply (ictcp_to_lms, in, lms); + + lms[0] = pq_eotf (lms[0]); + lms[1] = pq_eotf (lms[1]); + lms[2] = pq_eotf (lms[2]); + + multiply (lms_to_rec2020, lms, out); + + out[3] = in[3]; +} + +static void +color_map (GdkColorState *src_color_state, + GdkHdrMetadata *src_metadata, + GdkColorState *target_color_state, + GdkHdrMetadata *target_metadata, + float (*values)[4], + gsize n_values, + int debug) +{ + float ictcp[4]; + float ref_lum = 203; + float src_max_lum; + float target_max_lum; + float src_lum; + float needed_range; + float added_range; + float new_ref_lum; + float rel_highlight; + float low; + float high; + + src_max_lum = src_metadata->max_lum; + target_max_lum = target_metadata->max_lum; + + if (src_max_lum <= target_max_lum * 1.01 && + gdk_color_state_equal (src_color_state, target_color_state)) + { + return; + } + + needed_range = src_max_lum / ref_lum; + added_range = MIN (needed_range, 1.5); + new_ref_lum = ref_lum / added_range; + + for (gsize i = 0; i < n_values; i++) + { + GdkColor tmp; + float v[4]; + + if (src_max_lum <= target_max_lum * 1.01) + { + gdk_color_init (&tmp, src_color_state, values[i]); + gdk_color_to_float (&tmp, target_color_state, values[i]); + gdk_color_finish (&tmp); + } + else + { + gdk_color_init (&tmp, src_color_state, values[i]); + gdk_color_to_float (&tmp, gdk_color_state_get_rec2100_linear (), v); + gdk_color_finish (&tmp); + + rec2100_linear_to_ictcp (v, ictcp); + src_lum = pq_eotf (ictcp[0]) * 10000; + low = MIN (src_lum / added_range, new_ref_lum); + rel_highlight = CLAMP ((src_lum - new_ref_lum) / (src_max_lum - new_ref_lum), 0, 1); + high = pow (rel_highlight, 0.5) * (target_max_lum - new_ref_lum); + + if (debug > -1 && debug == i) + { + g_print ("needed range: %f\n", needed_range); + g_print ("added range: %f\n", added_range); + g_print ("new ref lum: %f\n", new_ref_lum); + g_print ("rec2100-linear: %f %f %f\n", v[0], v[1], v[2]); + g_print ("ictcp: %f %f %f\n", ictcp[0], ictcp[1], ictcp[2]); + g_print ("lum: %f\n", src_lum); + g_print ("adjusted lum: %f\n", low + high); + } + + ictcp[0] = pq_oetf ((low + high) / 10000); + ictcp_to_rec2100_linear (ictcp, v); + + gdk_color_init (&tmp, gdk_color_state_get_rec2100_linear (), v); + gdk_color_to_float (&tmp, target_color_state, values[i]); + + values[i][0] = CLAMP (values[i][0], 0, 1); + values[i][1] = CLAMP (values[i][1], 0, 1); + values[i][2] = CLAMP (values[i][2], 0, 1); + values[i][3] = CLAMP (values[i][3], 0, 1); + + if (debug > -1 && debug == i) + { + g_print ("final color: %f %f %f %f\n", + values[i][0], + values[i][1], + values[i][2], + values[i][3]); + } + + gdk_color_finish (&tmp); + } + } +} + +// END TEMPORARY + + static void save_image (const char *filename, const char *output, GdkMemoryFormat format, - GdkColorState *color_state) + GdkColorState *color_state, + GdkHdrMetadata *metadata) { GdkTexture *orig; + GdkColorState *orig_color_state; + GdkHdrMetadata *orig_metadata; + gsize width, height; GdkTextureDownloader *downloader; GBytes *bytes; gsize stride; @@ -44,20 +220,68 @@ save_image (const char *filename, GdkMemoryTextureBuilder *builder; orig = load_image_file (filename); + width = gdk_texture_get_width (orig); + height = gdk_texture_get_height (orig); + orig_color_state = gdk_texture_get_color_state (orig); + orig_metadata = gdk_texture_get_hdr_metadata (orig); + + if (metadata && orig_metadata) + { + guchar *data; + + downloader = gdk_texture_downloader_new (orig); + + gdk_texture_downloader_set_format (downloader, GDK_MEMORY_R32G32B32A32_FLOAT); + gdk_texture_downloader_set_color_state (downloader, orig_color_state); + bytes = gdk_texture_downloader_download_bytes (downloader, &stride); + gdk_texture_downloader_free (downloader); + + data = (guchar *) g_bytes_get_data (bytes, NULL); + + for (gsize i = 0; i < height; i++) + { + float (*row)[4]; + int debug; + + if (i == height / 2) + debug = width / 2; + else + debug = -1; + + row = (gpointer) (data + i * stride); + color_map (orig_color_state, orig_metadata, color_state, metadata, row, width, debug); + } + + builder = gdk_memory_texture_builder_new (); + gdk_memory_texture_builder_set_bytes (builder, bytes); + gdk_memory_texture_builder_set_stride (builder, stride); + gdk_memory_texture_builder_set_format (builder, GDK_MEMORY_R32G32B32A32_FLOAT); + gdk_memory_texture_builder_set_color_state (builder, color_state); + gdk_memory_texture_builder_set_hdr_metadata (builder, metadata); + gdk_memory_texture_builder_set_width (builder, width); + gdk_memory_texture_builder_set_height (builder, height); + + g_object_unref (orig); + orig = gdk_memory_texture_builder_build (builder); + g_object_unref (builder); + + } + downloader = gdk_texture_downloader_new (orig); gdk_texture_downloader_set_format (downloader, format); gdk_texture_downloader_set_color_state (downloader, color_state); - bytes = gdk_texture_downloader_download_bytes (downloader, &stride); + gdk_texture_downloader_free (downloader); builder = gdk_memory_texture_builder_new (); gdk_memory_texture_builder_set_bytes (builder, bytes); gdk_memory_texture_builder_set_stride (builder, stride); gdk_memory_texture_builder_set_format (builder, format); gdk_memory_texture_builder_set_color_state (builder, color_state); - gdk_memory_texture_builder_set_width (builder, gdk_texture_get_width (orig)); - gdk_memory_texture_builder_set_height (builder, gdk_texture_get_height (orig)); + gdk_memory_texture_builder_set_hdr_metadata (builder, metadata); + gdk_memory_texture_builder_set_width (builder, width); + gdk_memory_texture_builder_set_height (builder, height); texture = gdk_memory_texture_builder_build (builder); @@ -68,11 +292,44 @@ save_image (const char *filename, g_object_unref (texture); g_bytes_unref (bytes); - gdk_texture_downloader_free (downloader); g_object_unref (orig); g_object_unref (builder); } +static gboolean +parse_lum (const char *lum, + float l[2]) +{ + char **s; + char *endptr0 = NULL; + char *endptr1 = NULL; + + s = g_strsplit (lum, "-", 0); + g_print ("lum: '%s' '%s'\n", s[0], s[1]); + l[0] = g_ascii_strtod (s[0], &endptr0); + l[1] = g_ascii_strtod (s[1], &endptr1); + g_strfreev (s); + + return (endptr0 == NULL || endptr0[0] == '\0') && + (endptr1 == NULL || endptr1[0] == '\0'); +} + +static gboolean +find_primaries (GdkColorState *color_state, + float p[8]) +{ + if (gdk_color_state_equal (color_state, gdk_color_state_get_srgb ())) + { + p[0] = 0.640; p[1] = 0.330; + p[2] = 0.300; p[3] = 0.600; + p[4] = 0.150; p[5] = 0.060; + p[6] = 0.3127; p[7] = 0.3290; + return TRUE; + } + else + return FALSE; +} + void do_convert (int *argc, const char ***argv) @@ -82,9 +339,13 @@ do_convert (int *argc, char *format_name = NULL; char *colorstate_name = NULL; char *cicp_tuple = NULL; + char *mdcv_name = NULL; + char *lum = NULL; const GOptionEntry entries[] = { { "format", 0, 0, G_OPTION_ARG_STRING, &format_name, N_("Format to use"), N_("FORMAT") }, { "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") }, + { "mdcv", 0, 0, G_OPTION_ARG_STRING, &mdcv_name, N_("MDCV to use"), N_("COLORSTATE") }, + { "luminance", 0, 0, G_OPTION_ARG_STRING, &lum, N_("Luminance range"), N_("MIN-MAX") }, { "cicp", 0, 0, G_OPTION_ARG_STRING, &cicp_tuple, N_("Color state to use, as cicp tuple"), N_("CICP") }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILEā€¦") }, { NULL, } @@ -92,6 +353,7 @@ do_convert (int *argc, GError *error = NULL; GdkMemoryFormat format = GDK_MEMORY_DEFAULT; GdkColorState *color_state = NULL; + GdkHdrMetadata *hdr_metadata = NULL; g_set_prgname ("gtk4-image-tool convert"); context = g_option_context_new (NULL); @@ -153,6 +415,38 @@ do_convert (int *argc, } } + if (mdcv_name) + { + float primaries[8]; + float luminances[2]; + + if (lum && !parse_lum (lum, luminances)) + { + g_printerr ("Could not parse luminances\n"); + exit (1); + } + + color_state = find_color_state_by_name (mdcv_name); + if (mdcv_name && !color_state) + { + g_printerr ("MDCV not found\n"); + exit (1); + } + + if (!find_primaries (color_state, primaries)) + { + g_printerr ("Sorry no primaries\n"); + exit (1); + } + + hdr_metadata = gdk_hdr_metadata_new (primaries[0], primaries[1], + primaries[2], primaries[3], + primaries[4], primaries[5], + primaries[6], primaries[7], + luminances[0], luminances[1], + 0, 0); + } + if (cicp_tuple) { if (color_state) @@ -173,7 +467,7 @@ do_convert (int *argc, if (!color_state) color_state = gdk_color_state_get_srgb (); - save_image (filenames[0], filenames[1], format, color_state); + save_image (filenames[0], filenames[1], format, color_state, hdr_metadata); g_strfreev (filenames); } diff --git a/tools/meson.build b/tools/meson.build index a5cfd739f0..1c902119ab 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -56,7 +56,7 @@ gtk_tools = [ 'gtk-image-tool-relabel.c', 'gtk-image-tool-show.c', 'gtk-image-tool-utils.c', - '../testsuite/reftests/reftest-compare.c'], [libgtk_dep] ], + '../testsuite/reftests/reftest-compare.c'], [libgtk_static_dep] ], ['gtk4-update-icon-cache', ['updateiconcache.c', '../gtk/gtkiconcachevalidator.c' ] + extra_update_icon_cache_objs, [ libgtk_dep ] ], ['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ], ]