From 5ae225fe52460b97ade5afb470e5b50a17f1114e Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Sat, 22 Jun 2024 14:12:11 -0400 Subject: [PATCH] Add an image tool This is meant to provide a convenient way to produce images in specific color states and memory formats. --- docs/reference/gtk/gtk4-image-tool.rst | 82 +++++++++ docs/reference/gtk/meson.build | 1 + tools/gtk-image-tool-compare.c | 121 ++++++++++++++ tools/gtk-image-tool-convert.c | 157 +++++++++++++++++ tools/gtk-image-tool-info.c | 103 ++++++++++++ tools/gtk-image-tool-relabel.c | 137 +++++++++++++++ tools/gtk-image-tool-show.c | 129 ++++++++++++++ tools/gtk-image-tool-utils.c | 223 +++++++++++++++++++++++++ tools/gtk-image-tool.c | 127 ++++++++++++++ tools/gtk-image-tool.h | 19 +++ tools/meson.build | 8 + 11 files changed, 1107 insertions(+) create mode 100644 docs/reference/gtk/gtk4-image-tool.rst create mode 100644 tools/gtk-image-tool-compare.c create mode 100644 tools/gtk-image-tool-convert.c create mode 100644 tools/gtk-image-tool-info.c create mode 100644 tools/gtk-image-tool-relabel.c create mode 100644 tools/gtk-image-tool-show.c create mode 100644 tools/gtk-image-tool-utils.c create mode 100644 tools/gtk-image-tool.c create mode 100644 tools/gtk-image-tool.h diff --git a/docs/reference/gtk/gtk4-image-tool.rst b/docs/reference/gtk/gtk4-image-tool.rst new file mode 100644 index 0000000000..454e058da1 --- /dev/null +++ b/docs/reference/gtk/gtk4-image-tool.rst @@ -0,0 +1,82 @@ +.. _gtk4-image-tool(1): + +==================== +gtk4-image-tool +==================== + +----------------------- +Image Utility +----------------------- + +:Version: GTK +:Manual section: 1 +:Manual group: GTK commands + +SYNOPSIS +-------- +| **gtk4-image-tool** [OPTIONS...] ... +| +| **gtk4-image-tool** compare [OPTIONS...] +| **gtk4-image-tool** convert [OPTIONS...] +| **gtk4-image-tool** info [OPTIONS...] +| **gtk4-image-tool** relabel [OPTIONS...] +| **gtk4-image-tool** show [OPTIONS...] + +DESCRIPTION +----------- + +``gtk4-image-tool`` can perform various operations on images. + +COMMANDS +-------- + +Information +^^^^^^^^^^^ + +The ``info`` command shows general information about the image, such +as its format and color state. + +Showing +^^^^^^^ + +The ``show`` command displays the image. + +Compare +^^^^^^^ + +The ``compare`` command compares two images. If any differences are found, +the exit code is 1. If the images are identical, it is 0. + +``--output=FILE`` + + Save the differences as a png image in ``FILE``. + +``--quiet`` + + Don't write results to stdout. + +Conversion +^^^^^^^^^^ + +The ``convert`` command converts the image to a different format or color state. + +``--format=FORMAT`` + + Convert to the given format. The supported formats can be listed + with ``--format=list``. + +``--color-state=COLORSTATE`` + + Convert to the given color state. The supported color states can be + listed with ``--format=list``. + +Relabeling +^^^^^^^^^^ + +The ``relabel`` command changes the color state of an image without conversion. +This can be useful to produce wrong color renderings for diagnostics. + +``--color-state=COLORSTATE`` + + Convert to the given color state. The supported color states can be + listed with ``--format=list``. diff --git a/docs/reference/gtk/meson.build b/docs/reference/gtk/meson.build index c25d9a9f69..c4795c10dd 100644 --- a/docs/reference/gtk/meson.build +++ b/docs/reference/gtk/meson.build @@ -85,6 +85,7 @@ rst_files = [ [ 'gtk4-broadwayd', '1' ], [ 'gtk4-builder-tool', '1' ], [ 'gtk4-encode-symbolic-svg', '1', ], + [ 'gtk4-image-tool', '1' ], [ 'gtk4-launch', '1', ], [ 'gtk4-query-settings', '1', ], [ 'gtk4-rendernode-tool', '1' ], diff --git a/tools/gtk-image-tool-compare.c b/tools/gtk-image-tool-compare.c new file mode 100644 index 0000000000..79de7895e2 --- /dev/null +++ b/tools/gtk-image-tool-compare.c @@ -0,0 +1,121 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + +#include "testsuite/reftests/reftest-compare.h" + +void +do_compare (int *argc, + const char ***argv) +{ + GOptionContext *context; + gboolean opt_quiet = FALSE; + char **filenames = NULL; + char *opt_filename = NULL; + const GOptionEntry entries[] = { + { "output", 'o', 0, G_OPTION_ARG_FILENAME, &opt_filename, N_("Output file"), N_("FILE") }, + { "quiet", 'q', 0, G_OPTION_ARG_NONE, &opt_quiet, "Don't talk", NULL }, + + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") }, + { NULL, } + }; + GdkTexture *texture[2]; + GdkTexture *diff; + GError *error = NULL; + + g_set_prgname ("gtk4-image-tool compare"); + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_set_summary (context, _("Compare two images")); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr (_("No image file specified\n")); + exit (1); + } + + if (g_strv_length (filenames) != 2) + { + g_printerr (_("Can only accept two image files\n")); + exit (1); + } + + for (int i = 0; i < 2; i++) + { + texture[i] = gdk_texture_new_from_filename (filenames[i], &error); + if (texture[i] == NULL) + { + g_printerr (_("Failed to load %s: %s\n"), filenames[i], error->message); + exit (1); + } + } + + diff = reftest_compare_textures (texture[0], texture[1]); + + if (opt_filename && diff) + { + if (!gdk_texture_save_to_png (diff, opt_filename)) + { + g_printerr (_("Could not save diff image to %s\n"), opt_filename); + exit (1); + } + } + + if (!opt_quiet) + { + if (diff) + { + if (opt_filename) + g_print (_("Differences witten to %s.\n"), opt_filename); + else + g_print (_("The images are different.\n")); + } + else + g_print (_("No differences.\n")); + } + + if (diff) + exit (1); + + g_strfreev (filenames); + g_free (opt_filename); + g_object_unref (texture[0]); + g_object_unref (texture[1]); +} diff --git a/tools/gtk-image-tool-convert.c b/tools/gtk-image-tool-convert.c new file mode 100644 index 0000000000..adac555ce8 --- /dev/null +++ b/tools/gtk-image-tool-convert.c @@ -0,0 +1,157 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + + +static void +save_image (const char *filename, + const char *output, + GdkMemoryFormat format, + GdkColorState *color_state) +{ + GdkTexture *orig; + GdkTextureDownloader *downloader; + GBytes *bytes; + gsize stride; + GdkTexture *texture; + GdkMemoryTextureBuilder *builder; + + orig = load_image_file (filename); + 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); + + 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)); + + texture = gdk_memory_texture_builder_build (builder); + + if (g_str_has_suffix (output, ".tiff")) + gdk_texture_save_to_tiff (texture, output); + else + gdk_texture_save_to_png (texture, output); + + g_object_unref (texture); + g_bytes_unref (bytes); + gdk_texture_downloader_free (downloader); + g_object_unref (orig); + g_object_unref (builder); +} + +void +do_convert (int *argc, + const char ***argv) +{ + GOptionContext *context; + char **filenames = NULL; + char *format_name = NULL; + char *colorstate_name = 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") }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") }, + { NULL, } + }; + GError *error = NULL; + GdkMemoryFormat format = GDK_MEMORY_DEFAULT; + GdkColorState *color_state = gdk_color_state_get_srgb (); + + g_set_prgname ("gtk4-image-tool convert"); + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_set_summary (context, _("Convert the image to a different format or color state.")); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr (_("No image file specified\n")); + exit (1); + } + + if (g_strv_length (filenames) != 2) + { + g_printerr (_("Can only accept a single image file and output file\n")); + exit (1); + } + + if (format_name) + { + if (!find_format_by_name (format_name, &format)) + { + char **names; + char *suggestion; + + names = get_format_names (); + suggestion = g_strjoinv ("\n ", names); + + g_printerr (_("Not a memory format: %s\nPossible values:\n %s\n"), + format_name, suggestion); + exit (1); + } + } + + if (colorstate_name) + { + color_state = find_color_state_by_name (colorstate_name); + if (!color_state) + { + char **names; + char *suggestion; + + names = get_color_state_names (); + suggestion = g_strjoinv ("\n ", names); + + g_printerr (_("Not a color state: %s\nPossible values:\n %s\n"), + colorstate_name, suggestion); + exit (1); + } + } + + save_image (filenames[0], filenames[1], format, color_state); + + g_strfreev (filenames); +} diff --git a/tools/gtk-image-tool-info.c b/tools/gtk-image-tool-info.c new file mode 100644 index 0000000000..2f80cb622a --- /dev/null +++ b/tools/gtk-image-tool-info.c @@ -0,0 +1,103 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + +static const char * +get_format_name (GdkMemoryFormat format) +{ + GEnumClass *class; + GEnumValue *value; + const char *name; + + class = g_type_class_ref (GDK_TYPE_MEMORY_FORMAT); + value = g_enum_get_value (class, format); + name = value->value_nick; + g_type_class_unref (class); + + return name; +} + +static void +file_info (const char *filename) +{ + GdkTexture *texture; + + texture = load_image_file (filename); + + g_print ("%s %dx%d\n", _("Size:"), gdk_texture_get_width (texture), gdk_texture_get_height (texture)); + g_print ("%s %s\n", _("Format:"), get_format_name (gdk_texture_get_format (texture))); + g_print ("%s %s\n", _("Color state:"), get_color_state_name (gdk_texture_get_color_state (texture))); + + g_object_unref (texture); +} + +void +do_info (int *argc, + const char ***argv) +{ + GOptionContext *context; + char **filenames = NULL; + const GOptionEntry entries[] = { + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") }, + { NULL, } + }; + GError *error = NULL; + + g_set_prgname ("gtk4-image-tool info"); + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_set_summary (context, _("Provide information about the image.")); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr (_("No image file specified\n")); + exit (1); + } + + if (g_strv_length (filenames) > 1) + { + g_printerr (_("Can only accept a single image file\n")); + exit (1); + } + + file_info (filenames[0]); + + g_strfreev (filenames); +} diff --git a/tools/gtk-image-tool-relabel.c b/tools/gtk-image-tool-relabel.c new file mode 100644 index 0000000000..c068cddf03 --- /dev/null +++ b/tools/gtk-image-tool-relabel.c @@ -0,0 +1,137 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + + +static void +relabel_image (const char *filename, + const char *output, + GdkColorState *color_state) +{ + GdkTexture *orig; + GdkTextureDownloader *downloader; + GBytes *bytes; + gsize stride; + GdkTexture *texture; + GdkMemoryTextureBuilder *builder; + + orig = load_image_file (filename); + downloader = gdk_texture_downloader_new (orig); + + gdk_texture_downloader_set_format (downloader, gdk_texture_get_format (orig)); + gdk_texture_downloader_set_color_state (downloader, gdk_texture_get_color_state (orig)); + + bytes = gdk_texture_downloader_download_bytes (downloader, &stride); + + 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_width (builder, gdk_texture_get_width (orig)); + gdk_memory_texture_builder_set_height (builder, gdk_texture_get_height (orig)); + gdk_memory_texture_builder_set_format (builder, gdk_texture_get_format (orig)); + gdk_memory_texture_builder_set_color_state (builder, color_state); + + texture = gdk_memory_texture_builder_build (builder); + + if (g_str_has_suffix (output, ".tiff")) + gdk_texture_save_to_tiff (texture, output); + else + gdk_texture_save_to_png (texture, output); + + g_object_unref (texture); + g_bytes_unref (bytes); + gdk_texture_downloader_free (downloader); + g_object_unref (orig); + g_object_unref (builder); +} + +void +do_relabel (int *argc, + const char ***argv) +{ + GOptionContext *context; + char **filenames = NULL; + char *colorstate_name = NULL; + const GOptionEntry entries[] = { + { "color-state", 0, 0, G_OPTION_ARG_STRING, &colorstate_name, N_("Color state to use"), N_("COLORSTATE") }, + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE…") }, + { NULL, } + }; + GError *error = NULL; + GdkColorState *color_state = gdk_color_state_get_srgb (); + + g_set_prgname ("gtk4-image-tool relabel"); + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_set_summary (context, _("Change the color state of the image without conversion.")); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr (_("No image file specified\n")); + exit (1); + } + + if (g_strv_length (filenames) != 2) + { + g_printerr (_("Can only accept a single image file and output file\n")); + exit (1); + } + + if (colorstate_name) + { + color_state = find_color_state_by_name (colorstate_name); + if (!color_state) + { + char **names; + char *suggestion; + + names = get_color_state_names (); + suggestion = g_strjoinv (", ", names); + + g_printerr (_("Not a color state: %s\nPossible values: %s\n"), + colorstate_name, suggestion); + exit (1); + } + } + + relabel_image (filenames[0], filenames[1], color_state); + + g_strfreev (filenames); +} diff --git a/tools/gtk-image-tool-show.c b/tools/gtk-image-tool-show.c new file mode 100644 index 0000000000..222cddf147 --- /dev/null +++ b/tools/gtk-image-tool-show.c @@ -0,0 +1,129 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + +static void +set_window_title (GtkWindow *window, + const char *filename) +{ + char *name; + + name = g_path_get_basename (filename); + gtk_window_set_title (window, name); + g_free (name); +} + +static void +quit_cb (GtkWidget *widget, + gpointer user_data) +{ + gboolean *is_done = user_data; + + *is_done = TRUE; + + g_main_context_wakeup (NULL); +} + +static void +show_file (const char *filename) +{ + GdkTexture *texture; + GtkWidget *sw; + GtkWidget *window; + gboolean done = FALSE; + GtkWidget *picture; + + texture = load_image_file (filename); + + picture = gtk_picture_new_for_paintable (GDK_PAINTABLE (texture)); + gtk_picture_set_can_shrink (GTK_PICTURE (picture), FALSE); + gtk_picture_set_content_fit (GTK_PICTURE (picture), GTK_CONTENT_FIT_SCALE_DOWN); + + sw = gtk_scrolled_window_new (); + gtk_scrolled_window_set_propagate_natural_width (GTK_SCROLLED_WINDOW (sw), TRUE); + gtk_scrolled_window_set_propagate_natural_height (GTK_SCROLLED_WINDOW (sw), TRUE); + gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), picture); + + window = gtk_window_new (); + set_window_title (GTK_WINDOW (window), filename); + gtk_window_set_child (GTK_WINDOW (window), sw); + + gtk_window_present (GTK_WINDOW (window)); + g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done); + + while (!done) + g_main_context_iteration (NULL, TRUE); + + g_object_unref (texture); +} + +void +do_show (int *argc, + const char ***argv) +{ + GOptionContext *context; + char **filenames = NULL; + const GOptionEntry entries[] = { + { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, N_("FILE") }, + { NULL, } + }; + GError *error = NULL; + + g_set_prgname ("gtk4-image-tool show"); + context = g_option_context_new (NULL); + g_option_context_set_translation_domain (context, GETTEXT_PACKAGE); + g_option_context_add_main_entries (context, entries, NULL); + g_option_context_set_summary (context, _("Show the image.")); + + if (!g_option_context_parse (context, argc, (char ***)argv, &error)) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_option_context_free (context); + + if (filenames == NULL) + { + g_printerr (_("No image file specified\n")); + exit (1); + } + + if (g_strv_length (filenames) > 1) + { + g_printerr (_("Can only accept a single image file\n")); + exit (1); + } + + show_file (filenames[0]); + + g_strfreev (filenames); +} diff --git a/tools/gtk-image-tool-utils.c b/tools/gtk-image-tool-utils.c new file mode 100644 index 0000000000..e4cae80934 --- /dev/null +++ b/tools/gtk-image-tool-utils.c @@ -0,0 +1,223 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + + +GdkTexture * +load_image_file (const char *filename) +{ + GError *error = NULL; + GdkTexture *texture; + + texture = gdk_texture_new_from_filename (filename, &error); + if (!texture) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + return texture; +} + +gboolean +find_format_by_name (const char *name, + GdkMemoryFormat *format) +{ + GEnumClass *class; + GEnumValue *value; + + class = g_type_class_ref (GDK_TYPE_MEMORY_FORMAT); + + value = g_enum_get_value_by_nick (class, name); + if (value) + *format = value->value; + + g_type_class_unref (class); + + return value != NULL; +} + +char ** +get_format_names (void) +{ + GEnumClass *class; + GStrvBuilder *builder; + char **res; + + builder = g_strv_builder_new (); + class = g_type_class_ref (GDK_TYPE_MEMORY_FORMAT); + + for (int i = 0; i < class->n_values; i++) + { + if (class->values[i].value == GDK_MEMORY_N_FORMATS) + continue; + g_strv_builder_add (builder, class->values[i].value_nick); + } + + g_type_class_unref (class); + + res = g_strv_builder_end (builder); + + g_strv_builder_unref (builder); + + return res; +} + +GdkColorState * +find_color_state_by_name (const char *name) +{ + GdkCicpParams *params = NULL; + GdkColorState *color_state = NULL; + GError *error = NULL; + + if (g_strcmp0 (name, "srgb") == 0) + color_state = gdk_color_state_get_srgb (); + else if (g_strcmp0 (name, "srgb-linear") == 0) + color_state = gdk_color_state_get_srgb_linear (); + else if (g_strcmp0 (name, "rec2100-pq") == 0) + color_state = gdk_color_state_get_rec2100_pq (); + else if (g_strcmp0 (name, "rec2100-linear") == 0) + color_state = gdk_color_state_get_rec2100_linear (); + else if (g_strcmp0 (name, "display-p3") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 12); + gdk_cicp_params_set_transfer_function (params, 13); + gdk_cicp_params_set_matrix_coefficients (params, 0); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + else if (g_strcmp0 (name, "xyz") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 10); + gdk_cicp_params_set_transfer_function (params, 8); + gdk_cicp_params_set_matrix_coefficients (params, 0); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + else if (g_strcmp0 (name, "rec2020") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 9); + gdk_cicp_params_set_transfer_function (params, 1); + gdk_cicp_params_set_matrix_coefficients (params, 0); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + else if (g_strcmp0 (name, "rec2100-hlg") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 9); + gdk_cicp_params_set_transfer_function (params, 18); + gdk_cicp_params_set_matrix_coefficients (params, 0); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + else if (g_strcmp0 (name, "yuv") == 0 || + g_strcmp0 (name, "bt601") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 1); + gdk_cicp_params_set_transfer_function (params, 13); + gdk_cicp_params_set_matrix_coefficients (params, 6); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_NARROW); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + else if (g_strcmp0 (name, "bt709") == 0) + { + params = gdk_cicp_params_new (); + + gdk_cicp_params_set_color_primaries (params, 1); + gdk_cicp_params_set_transfer_function (params, 1); + gdk_cicp_params_set_matrix_coefficients (params, 6); + gdk_cicp_params_set_range (params, GDK_CICP_RANGE_NARROW); + + color_state = gdk_cicp_params_build_color_state (params, &error); + } + + if (error) + { + g_printerr ("%s\n", error->message); + g_error_free (error); + exit (1); + } + + g_clear_object (¶ms); + + return color_state; +} + +char ** +get_color_state_names (void) +{ + static const char *names[] = { + "srgb", "srgb-linear", "display-p3", "xyz", "rec2020", + "rec2100-pq", "rec2100-linear", "rec2100-hlg", + "yuv", "bt601", "bt709", + NULL, + }; + + return g_strdupv ((char **) names); +} + +char * +get_color_state_name (GdkColorState *color_state) +{ + char **names; + char *name = NULL; + + names = get_color_state_names (); + + for (int i = 0; names[i]; i++) + { + GdkColorState *cs = find_color_state_by_name (names[i]); + if (gdk_color_state_equal (cs, color_state)) + { + name = g_strdup (names[i]); + break; + } + } + + g_strfreev (names); + + return name; +} diff --git a/tools/gtk-image-tool.c b/tools/gtk-image-tool.c new file mode 100644 index 0000000000..de149eeb4f --- /dev/null +++ b/tools/gtk-image-tool.c @@ -0,0 +1,127 @@ +/* Copyright 2024 Red Hat, Inc. + * + * GTK is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * GTK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with GTK; see the file COPYING. If not, + * see . + * + * Author: Matthias Clasen + */ + +#include "config.h" + +#include +#include +#include + +#include +#include +#include +#include +#include "gtk-image-tool.h" + + +static void G_GNUC_NORETURN +usage (void) +{ + g_print (_("Usage:\n" + " gtk4-image-tool [COMMAND] [OPTION…] FILE…\n" + "\n" + "Perform various tasks on images.\n" + "\n" + "Commands:\n" + " compare Show differences between two images\n" + " convert Convert the image to a different format or color state\n" + " info Show general information about the image\n" + " relabel Change the color state of the image without conversion\n" + " show Show the image\n" + "\n")); + exit (1); +} + +static GLogWriterOutput +log_writer_func (GLogLevelFlags level, + const GLogField *fields, + gsize n_fields, + gpointer user_data) +{ + gsize i; + const char *domain = NULL; + const char *message = NULL; + + for (i = 0; i < n_fields; i++) + { + if (g_strcmp0 (fields[i].key, "GLIB_DOMAIN") == 0) + domain = fields[i].value; + else if (g_strcmp0 (fields[i].key, "MESSAGE") == 0) + message = fields[i].value; + } + + if (message != NULL && !g_log_writer_default_would_drop (level, domain)) + { + const char *prefix; + switch (level & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_ERROR: + prefix = "ERROR"; + break; + case G_LOG_LEVEL_CRITICAL: + prefix = "CRITICAL"; + break; + case G_LOG_LEVEL_WARNING: + prefix = "WARNING"; + break; + default: + prefix = "INFO"; + break; + } + g_printerr ("%s-%s: %s\n", domain, prefix, message); + } + + return G_LOG_WRITER_HANDLED; +} + +int +main (int argc, const char *argv[]) +{ + g_set_prgname ("gtk-image-tool"); + + g_log_set_writer_func (log_writer_func, NULL, NULL); + + gtk_init_check (); + + gtk_test_register_all_types (); + + if (argc < 2) + usage (); + + if (strcmp (argv[1], "--help") == 0) + usage (); + + argv++; + argc--; + + if (strcmp (argv[0], "compare") == 0) + do_compare (&argc, &argv); + else if (strcmp (argv[0], "convert") == 0) + do_convert (&argc, &argv); + else if (strcmp (argv[0], "info") == 0) + do_info (&argc, &argv); + else if (strcmp (argv[0], "relabel") == 0) + do_relabel (&argc, &argv); + else if (strcmp (argv[0], "show") == 0) + do_show (&argc, &argv); + else + usage (); + + return 0; +} diff --git a/tools/gtk-image-tool.h b/tools/gtk-image-tool.h new file mode 100644 index 0000000000..9203d6e551 --- /dev/null +++ b/tools/gtk-image-tool.h @@ -0,0 +1,19 @@ + +#pragma once + +void do_compare (int *argc, const char ***argv); +void do_convert (int *argc, const char ***argv); +void do_info (int *argc, const char ***argv); +void do_relabel (int *argc, const char ***argv); +void do_show (int *argc, const char ***argv); + +GdkTexture * load_image_file (const char *filename); + + +gboolean find_format_by_name (const char *name, + GdkMemoryFormat *format); +char ** get_format_names (void); +GdkColorState * find_color_state_by_name (const char *name); + +char ** get_color_state_names (void); +char * get_color_state_name (GdkColorState *color_state); diff --git a/tools/meson.build b/tools/meson.build index c9380f93f7..a5cfd739f0 100644 --- a/tools/meson.build +++ b/tools/meson.build @@ -49,6 +49,14 @@ gtk_tools = [ 'gtk-rendernode-tool-show.c', 'gtk-rendernode-tool-utils.c', '../testsuite/reftests/reftest-compare.c'], [libgtk_dep] ], + ['gtk4-image-tool', ['gtk-image-tool.c', + 'gtk-image-tool-info.c', + 'gtk-image-tool-compare.c', + 'gtk-image-tool-convert.c', + 'gtk-image-tool-relabel.c', + 'gtk-image-tool-show.c', + 'gtk-image-tool-utils.c', + '../testsuite/reftests/reftest-compare.c'], [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 ] ], ]