wip: Hack tone mapping into gtk4-image-tool

This is just for playing around, not intended to be merged.
This commit is contained in:
Matthias Clasen 2024-08-21 09:08:16 -04:00
parent df450ad353
commit c023762329
3 changed files with 313 additions and 7 deletions

View File

@ -16,3 +16,15 @@ struct _GdkHdrMetadata
float max_cll; float max_cll;
float max_fall; 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);
}

View File

@ -29,14 +29,190 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include "gtk-image-tool.h" #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 static void
save_image (const char *filename, save_image (const char *filename,
const char *output, const char *output,
GdkMemoryFormat format, GdkMemoryFormat format,
GdkColorState *color_state) GdkColorState *color_state,
GdkHdrMetadata *metadata)
{ {
GdkTexture *orig; GdkTexture *orig;
GdkColorState *orig_color_state;
GdkHdrMetadata *orig_metadata;
gsize width, height;
GdkTextureDownloader *downloader; GdkTextureDownloader *downloader;
GBytes *bytes; GBytes *bytes;
gsize stride; gsize stride;
@ -44,20 +220,68 @@ save_image (const char *filename,
GdkMemoryTextureBuilder *builder; GdkMemoryTextureBuilder *builder;
orig = load_image_file (filename); 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); downloader = gdk_texture_downloader_new (orig);
gdk_texture_downloader_set_format (downloader, format); gdk_texture_downloader_set_format (downloader, format);
gdk_texture_downloader_set_color_state (downloader, color_state); gdk_texture_downloader_set_color_state (downloader, color_state);
bytes = gdk_texture_downloader_download_bytes (downloader, &stride); bytes = gdk_texture_downloader_download_bytes (downloader, &stride);
gdk_texture_downloader_free (downloader);
builder = gdk_memory_texture_builder_new (); builder = gdk_memory_texture_builder_new ();
gdk_memory_texture_builder_set_bytes (builder, bytes); gdk_memory_texture_builder_set_bytes (builder, bytes);
gdk_memory_texture_builder_set_stride (builder, stride); gdk_memory_texture_builder_set_stride (builder, stride);
gdk_memory_texture_builder_set_format (builder, format); gdk_memory_texture_builder_set_format (builder, format);
gdk_memory_texture_builder_set_color_state (builder, color_state); 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_hdr_metadata (builder, metadata);
gdk_memory_texture_builder_set_height (builder, gdk_texture_get_height (orig)); gdk_memory_texture_builder_set_width (builder, width);
gdk_memory_texture_builder_set_height (builder, height);
texture = gdk_memory_texture_builder_build (builder); texture = gdk_memory_texture_builder_build (builder);
@ -68,11 +292,44 @@ save_image (const char *filename,
g_object_unref (texture); g_object_unref (texture);
g_bytes_unref (bytes); g_bytes_unref (bytes);
gdk_texture_downloader_free (downloader);
g_object_unref (orig); g_object_unref (orig);
g_object_unref (builder); 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 void
do_convert (int *argc, do_convert (int *argc,
const char ***argv) const char ***argv)
@ -82,9 +339,13 @@ do_convert (int *argc,
char *format_name = NULL; char *format_name = NULL;
char *colorstate_name = NULL; char *colorstate_name = NULL;
char *cicp_tuple = NULL; char *cicp_tuple = NULL;
char *mdcv_name = NULL;
char *lum = NULL;
const GOptionEntry entries[] = { const GOptionEntry entries[] = {
{ "format", 0, 0, G_OPTION_ARG_STRING, &format_name, N_("Format to use"), N_("FORMAT") }, { "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") }, { "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") }, { "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…") }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") },
{ NULL, } { NULL, }
@ -92,6 +353,7 @@ do_convert (int *argc,
GError *error = NULL; GError *error = NULL;
GdkMemoryFormat format = GDK_MEMORY_DEFAULT; GdkMemoryFormat format = GDK_MEMORY_DEFAULT;
GdkColorState *color_state = NULL; GdkColorState *color_state = NULL;
GdkHdrMetadata *hdr_metadata = NULL;
g_set_prgname ("gtk4-image-tool convert"); g_set_prgname ("gtk4-image-tool convert");
context = g_option_context_new (NULL); 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 (cicp_tuple)
{ {
if (color_state) if (color_state)
@ -173,7 +467,7 @@ do_convert (int *argc,
if (!color_state) if (!color_state)
color_state = gdk_color_state_get_srgb (); 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); g_strfreev (filenames);
} }

View File

@ -56,7 +56,7 @@ gtk_tools = [
'gtk-image-tool-relabel.c', 'gtk-image-tool-relabel.c',
'gtk-image-tool-show.c', 'gtk-image-tool-show.c',
'gtk-image-tool-utils.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-update-icon-cache', ['updateiconcache.c', '../gtk/gtkiconcachevalidator.c' ] + extra_update_icon_cache_objs, [ libgtk_dep ] ],
['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ], ['gtk4-encode-symbolic-svg', ['encodesymbolic.c'], [ libgtk_static_dep ] ],
] ]