From 4be9b7a401b8f981b4720f090c887b494e890726 Mon Sep 17 00:00:00 2001 From: Jehan Date: Wed, 6 Mar 2019 16:14:32 +0100 Subject: [PATCH] app, libgimpwidgets: new gimp_color_area_set_out_of_gamut(). This allows to force a GimpColorArea to display as out-of-gamut color. Current code was only considering the generic RGB case (outside of [0-1] range), and in particular not grayscale or indexed images. Ideally the GimpColorArea widget could be (optionally) made to follow a context, so that for instance it could update its representation when the context image changes, or when this image's type changes. Yet since it is a libgimpwidgets widget, it cannot get such update. Instead I add a new API function to display the color box with the out-of-gamut triangle. The decision code for this will have to be done elsewhere. Use this new API for GimpColorHistory to display non-gray colors in the history as out-of-gamut on grayscale images, or colors absent of the palette on indexed images. --- app/widgets/gimpcolorhistory.c | 107 +++++++++++++++++++++++++++++---- app/widgets/gimpcolorhistory.h | 1 + libgimpwidgets/gimpcolorarea.c | 41 ++++++++++++- libgimpwidgets/gimpcolorarea.h | 2 + 4 files changed, 135 insertions(+), 16 deletions(-) diff --git a/app/widgets/gimpcolorhistory.c b/app/widgets/gimpcolorhistory.c index 8e75570750..4e377dcc94 100644 --- a/app/widgets/gimpcolorhistory.c +++ b/app/widgets/gimpcolorhistory.c @@ -32,6 +32,8 @@ #include "core/gimp.h" #include "core/gimp-palettes.h" #include "core/gimpcontext.h" +#include "core/gimpimage.h" +#include "app/core/gimpimage-colormap.h" #include "core/gimpmarshal.h" #include "core/gimppalettemru.h" @@ -55,6 +57,7 @@ enum #define DEFAULT_HISTORY_SIZE 12 #define COLOR_AREA_SIZE 20 +#define CHANNEL_EPSILON 1e-3 /* GObject methods */ static void gimp_color_history_constructed (GObject *object); @@ -85,12 +88,15 @@ static void gimp_color_history_size_allocate (GtkWidget static void gimp_color_history_color_clicked (GtkWidget *widget, GimpColorHistory *history); -static void gimp_color_history_palette_dirty (GimpPalette *palette, - GimpColorHistory *history); +static void gimp_color_history_palette_dirty (GimpColorHistory *history); static void gimp_color_history_color_changed (GtkWidget *widget, gpointer data); +static void gimp_color_history_image_changed (GimpContext *context, + GimpImage *image, + GimpColorHistory *history); + /* Utils */ static void gimp_color_history_reorganize (GimpColorHistory *history); @@ -169,7 +175,7 @@ gimp_color_history_constructed (GObject *object) g_signal_connect_object (palette, "dirty", G_CALLBACK (gimp_color_history_palette_dirty), - G_OBJECT (history), 0); + G_OBJECT (history), G_CONNECT_SWAPPED); } static void @@ -196,15 +202,41 @@ gimp_color_history_set_property (GObject *object, switch (property_id) { case PROP_CONTEXT: + if (history->context) + g_signal_handlers_disconnect_by_func (history->context, + gimp_color_history_image_changed, + history); + if (history->active_image) + { + g_signal_handlers_disconnect_by_func (history->active_image, + G_CALLBACK (gimp_color_history_palette_dirty), + history); + history->active_image = NULL; + } history->context = g_value_get_object (value); + if (history->context) + { + g_signal_connect (history->context, "image-changed", + G_CALLBACK (gimp_color_history_image_changed), + history); + history->active_image = gimp_context_get_image (history->context); + if (history->active_image) + { + g_signal_connect_swapped (history->active_image, "notify::base-type", + G_CALLBACK (gimp_color_history_palette_dirty), + history); + g_signal_connect_swapped (history->active_image, "colormap-changed", + G_CALLBACK (gimp_color_history_palette_dirty), + history); + } + } break; case PROP_HISTORY_SIZE: { - GimpPalette *palette; - GtkWidget *button; - GtkWidget *color_area; - gint i; + GtkWidget *button; + GtkWidget *color_area; + gint i; history->history_size = g_value_get_int (value); @@ -249,8 +281,7 @@ gimp_color_history_set_property (GObject *object, history->color_areas[i] = color_area; } - palette = gimp_palettes_get_color_history (history->context->gimp); - gimp_color_history_palette_dirty (palette, history); + gimp_color_history_palette_dirty (history); } break; @@ -426,22 +457,47 @@ gimp_color_history_color_clicked (GtkWidget *widget, /* Color history palette callback. */ static void -gimp_color_history_palette_dirty (GimpPalette *palette, - GimpColorHistory *history) +gimp_color_history_palette_dirty (GimpColorHistory *history) { - gint i; + GimpPalette *palette; + GimpPalette *colormap_palette = NULL; + GimpImageBaseType base_type = GIMP_RGB; + gint i; + + palette = gimp_palettes_get_color_history (history->context->gimp); + if (history->active_image) + { + base_type = gimp_image_get_base_type (history->active_image); + if (base_type == GIMP_INDEXED) + colormap_palette = gimp_image_get_colormap_palette (history->active_image); + } for (i = 0; i < history->history_size; i++) { GimpPaletteEntry *entry = gimp_palette_get_entry (palette, i); GimpRGB black = { 0.0, 0.0, 0.0, 1.0 }; + GimpRGB color = entry ? entry->color : black; + gboolean oog = FALSE; g_signal_handlers_block_by_func (history->color_areas[i], gimp_color_history_color_changed, GINT_TO_POINTER (i)); gimp_color_area_set_color (GIMP_COLOR_AREA (history->color_areas[i]), - entry ? &entry->color : &black); + &color); + if (/* Common out-of-gamut case */ + (color.r < 0.0 || color.r > 1.0 || + color.g < 0.0 || color.g > 1.0 || + color.b < 0.0 || color.b > 1.0) || + /* Indexed images */ + (colormap_palette && ! gimp_palette_find_entry (colormap_palette, &color, NULL)) || + /* Grayscale images */ + (base_type == GIMP_GRAY && + (ABS (color.r - color.g) > CHANNEL_EPSILON || + ABS (color.r - color.b) > CHANNEL_EPSILON || + ABS (color.g - color.b) > CHANNEL_EPSILON))) + oog = TRUE; + gimp_color_area_set_out_of_gamut (GIMP_COLOR_AREA (history->color_areas[i]), oog); g_signal_handlers_unblock_by_func (history->color_areas[i], gimp_color_history_color_changed, @@ -469,6 +525,31 @@ gimp_color_history_color_changed (GtkWidget *widget, gimp_palette_set_entry_color (palette, GPOINTER_TO_INT (data), &color); } +static void +gimp_color_history_image_changed (GimpContext *context, + GimpImage *image, + GimpColorHistory *history) +{ + /* Update active image. */ + if (history->active_image) + g_signal_handlers_disconnect_by_func (history->active_image, + G_CALLBACK (gimp_color_history_palette_dirty), + history); + history->active_image = image; + if (image) + { + g_signal_connect_swapped (image, "notify::base-type", + G_CALLBACK (gimp_color_history_palette_dirty), + history); + g_signal_connect_swapped (image, "colormap-changed", + G_CALLBACK (gimp_color_history_palette_dirty), + history); + } + + /* Update the palette. */ + gimp_color_history_palette_dirty (history); +} + static void gimp_color_history_reorganize (GimpColorHistory *history) { diff --git a/app/widgets/gimpcolorhistory.h b/app/widgets/gimpcolorhistory.h index f0f5c65fd4..cd86196301 100644 --- a/app/widgets/gimpcolorhistory.h +++ b/app/widgets/gimpcolorhistory.h @@ -37,6 +37,7 @@ struct _GimpColorHistory GtkGrid parent_instance; GimpContext *context; + GimpImage *active_image; GtkWidget **color_areas; GtkWidget **buttons; diff --git a/libgimpwidgets/gimpcolorarea.c b/libgimpwidgets/gimpcolorarea.c index d0acf3dcfd..7684f9056c 100644 --- a/libgimpwidgets/gimpcolorarea.c +++ b/libgimpwidgets/gimpcolorarea.c @@ -80,6 +80,8 @@ struct _GimpColorAreaPrivate GimpRGB color; guint draw_border : 1; guint needs_render : 1; + + gboolean out_of_gamut; }; #define GET_PRIVATE(obj) (((GimpColorArea *) (obj))->priv) @@ -457,9 +459,10 @@ gimp_color_area_draw (GtkWidget *widget, } if (priv->config && - (priv->color.r < 0.0 || priv->color.r > 1.0 || - priv->color.g < 0.0 || priv->color.g > 1.0 || - priv->color.b < 0.0 || priv->color.b > 1.0)) + ((priv->color.r < 0.0 || priv->color.r > 1.0 || + priv->color.g < 0.0 || priv->color.g > 1.0 || + priv->color.b < 0.0 || priv->color.b > 1.0) || + priv->out_of_gamut)) { GimpRGB color; gint side = MIN (priv->width, priv->height) * 2 / 3; @@ -652,6 +655,38 @@ gimp_color_area_set_draw_border (GimpColorArea *area, } } +/** + * gimp_color_area_set_out_of_gamut: + * @area: a #GimpColorArea widget. + * @config: a #GimpColorConfig object. + * + * Sets the color area to render as an out-of-gamut color, i.e. with a + * small triangle on a corner using the color management out of gamut + * color (as per gimp_color_area_set_color_config()). + * + * By default, @area will render as out-of-gamut for any RGB color with + * a channel out of the [0; 1] range. This function allows to consider + * more colors out of gamut (for instance non-gray colors on a grayscale + * image, or colors absent of palettes in indexed images, etc.) + * + * Since: 2.10.10 + */ +void +gimp_color_area_set_out_of_gamut (GimpColorArea *area, + gboolean out_of_gamut) +{ + GimpColorAreaPrivate *priv; + + g_return_if_fail (GIMP_IS_COLOR_AREA (area)); + + priv = GET_PRIVATE (area); + if (priv->out_of_gamut != out_of_gamut) + { + priv->out_of_gamut = out_of_gamut; + gtk_widget_queue_draw (GTK_WIDGET (area)); + } +} + /** * gimp_color_area_set_color_config: * @area: a #GimpColorArea widget. diff --git a/libgimpwidgets/gimpcolorarea.h b/libgimpwidgets/gimpcolorarea.h index aed62c4f58..693ae4e159 100644 --- a/libgimpwidgets/gimpcolorarea.h +++ b/libgimpwidgets/gimpcolorarea.h @@ -86,6 +86,8 @@ void gimp_color_area_set_type (GimpColorArea *area, GimpColorAreaType type); void gimp_color_area_set_draw_border (GimpColorArea *area, gboolean draw_border); +void gimp_color_area_set_out_of_gamut (GimpColorArea *area, + gboolean out_of_gamut); void gimp_color_area_set_color_config (GimpColorArea *area, GimpColorConfig *config);