diff --git a/app/dialogs/preferences-dialog.c b/app/dialogs/preferences-dialog.c index 4f89ad7e57..24e85c1777 100644 --- a/app/dialogs/preferences-dialog.c +++ b/app/dialogs/preferences-dialog.c @@ -1527,11 +1527,11 @@ prefs_dialog_new (Gimp *gimp, _("Mar_k out of gamut colors")); gtk_box_pack_start (GTK_BOX (hbox), button, TRUE, TRUE, 0); - button = gimp_prop_color_button_new (color_config, "out-of-gamut-color", - _("Select Warning Color"), - PREFS_COLOR_BUTTON_WIDTH, - PREFS_COLOR_BUTTON_HEIGHT, - GIMP_COLOR_AREA_FLAT); + button = gimp_prop_gegl_color_button_new (color_config, "out-of-gamut-color", + _("Select Warning Color"), + PREFS_COLOR_BUTTON_WIDTH, + PREFS_COLOR_BUTTON_HEIGHT, + GIMP_COLOR_AREA_FLAT); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); gimp_color_panel_set_context (GIMP_COLOR_PANEL (button), diff --git a/app/display/gimpdisplayshell-draw.c b/app/display/gimpdisplayshell-draw.c index 950fd56542..0be72f23d7 100644 --- a/app/display/gimpdisplayshell-draw.c +++ b/app/display/gimpdisplayshell-draw.c @@ -89,10 +89,13 @@ gimp_display_shell_draw_background (GimpDisplayShell *shell, if (canvas->padding_mode != GIMP_CANVAS_PADDING_MODE_DEFAULT) { - GimpRGB rgb; + GimpColorConfig *config = gimp_display_shell_get_color_config (shell); - gegl_color_get_pixel (canvas->padding_color, babl_format ("R'G'B'A double"), &rgb); - gimp_cairo_set_source_rgb (cr, &rgb); + /* Padding color is color-managed to shell's monitor profile but not + * soft-proofed. + */ + gimp_cairo_set_source_color (cr, canvas->padding_color, config, FALSE, + GTK_WIDGET (shell)); cairo_paint (cr); } } diff --git a/app/widgets/gimpfgbgeditor.c b/app/widgets/gimpfgbgeditor.c index 9864e05d79..3eea981b66 100644 --- a/app/widgets/gimpfgbgeditor.c +++ b/app/widgets/gimpfgbgeditor.c @@ -892,7 +892,6 @@ gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor, gdouble srgb_color[4]; gdouble transformed_color[4]; gboolean is_out_of_gamut; - const Babl *target_space = NULL; if (editor->active_image) { @@ -931,53 +930,58 @@ gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor, cairo_fill (cr); if (editor->active_image) - target_space = gimp_image_get_layer_space (editor->active_image); - else - target_space = babl_space ("sRGB"); - - if (base_type == GIMP_GRAY) { - gfloat gray[1]; + const Babl *target_space = gimp_image_get_layer_space (editor->active_image); - gegl_color_get_pixel (color, - babl_format_with_space ("Y' float", target_space), - gray); - is_out_of_gamut = ((gray[0] < 0.0 && -gray[0] > CHANNEL_EPSILON) || - (gray[0] > 1.0 && gray[0] - 1.0 > CHANNEL_EPSILON)); + if (base_type == GIMP_GRAY) + { + gfloat gray[1]; - if (! is_out_of_gamut) + gegl_color_get_pixel (color, + babl_format_with_space ("Y' float", target_space), + gray); + is_out_of_gamut = ((gray[0] < 0.0 && -gray[0] > CHANNEL_EPSILON) || + (gray[0] > 1.0 && gray[0] - 1.0 > CHANNEL_EPSILON)); + + if (! is_out_of_gamut) + { + gdouble rgb[3]; + + /* Grayscale colors can be out of gamut if the color is out of the [0; + * 1] range in the target space and also if they can be converted to + * RGB with non-equal components. + */ + gegl_color_get_pixel (color, + babl_format_with_space ("R'G'B' double", target_space), + rgb); + is_out_of_gamut = (ABS (rgb[0] - rgb[0]) > CHANNEL_EPSILON || + ABS (rgb[1] - rgb[1]) > CHANNEL_EPSILON || + ABS (rgb[2] - rgb[2]) > CHANNEL_EPSILON); + } + } + else { gdouble rgb[3]; - /* Grayscale colors can be out of gamut if the color is out of the [0; - * 1] range in the target space and also if they can be converted to - * RGB with non-equal components. - */ gegl_color_get_pixel (color, - babl_format_with_space ("R'G'B' float", target_space), + babl_format_with_space ("R'G'B' double", target_space), rgb); - is_out_of_gamut = (ABS (rgb[0] - rgb[0]) > CHANNEL_EPSILON || - ABS (rgb[1] - rgb[1]) > CHANNEL_EPSILON || - ABS (rgb[2] - rgb[2]) > CHANNEL_EPSILON); + /* We make sure that each component is within [0; 1], but accept a small + * error of margin (we don't want to show small precision errors as + * out-of-gamut colors). + */ + is_out_of_gamut = ((rgb[0] < 0.0 && -rgb[0] > CHANNEL_EPSILON) || + (rgb[0] > 1.0 && rgb[0] - 1.0 > CHANNEL_EPSILON) || + (rgb[1] < 0.0 && -rgb[1] > CHANNEL_EPSILON) || + (rgb[1] > 1.0 && rgb[1] - 1.0 > CHANNEL_EPSILON) || + (rgb[2] < 0.0 && -rgb[2] > CHANNEL_EPSILON) || + (rgb[2] > 1.0 && rgb[2] - 1.0 > CHANNEL_EPSILON)); } } else { - gdouble rgb[3]; - - gegl_color_get_pixel (color, - babl_format_with_space ("R'G'B' float", target_space), - rgb); - /* We make sure that each component is within [0; 1], but accept a small - * error of margin (we don't want to show small precision errors as - * out-of-gamut colors). - */ - is_out_of_gamut = ((rgb[0] < 0.0 && -rgb[0] > CHANNEL_EPSILON) || - (rgb[0] > 1.0 && rgb[0] - 1.0 > CHANNEL_EPSILON) || - (rgb[1] < 0.0 && -rgb[1] > CHANNEL_EPSILON) || - (rgb[1] > 1.0 && rgb[1] - 1.0 > CHANNEL_EPSILON) || - (rgb[2] < 0.0 && -rgb[2] > CHANNEL_EPSILON) || - (rgb[2] > 1.0 && rgb[2] - 1.0 > CHANNEL_EPSILON)); + /* Without active image, we are never out of gamut. */ + is_out_of_gamut = FALSE; } if (editor->color_config && @@ -987,20 +991,24 @@ gimp_fg_bg_editor_draw_color_frame (GimpFgBgEditor *editor, (colormap_palette && ! gimp_palette_find_entry (colormap_palette, color, NULL)))) { - gint corner_x = x + 0.5 * (1.0 - corner_dx) * width; - gint corner_y = y + 0.5 * (1.0 - corner_dy) * height; - gint side = MIN (width, height) * 2 / 3; - GimpRGB out_of_gamut_color; + GeglColor *out_of_gamut_color; + gint corner_x = x + 0.5 * (1.0 - corner_dx) * width; + gint corner_y = y + 0.5 * (1.0 - corner_dy) * height; + gint side = MIN (width, height) * 2 / 3; cairo_move_to (cr, corner_x, corner_y); cairo_line_to (cr, corner_x + side * corner_dx, corner_y); cairo_line_to (cr, corner_x, corner_y + side * corner_dy); cairo_close_path (cr); - gimp_color_config_get_out_of_gamut_color (editor->color_config, - &out_of_gamut_color); - gimp_cairo_set_source_rgb (cr, &out_of_gamut_color); + out_of_gamut_color = gimp_color_config_get_out_of_gamut_color (editor->color_config); + /* Out-of-gamut color is color-managed to FG/BG editor's monitor profile + * but not soft-proofed. + */ + gimp_cairo_set_source_color (cr, out_of_gamut_color, editor->color_config, FALSE, GTK_WIDGET (editor)); cairo_fill (cr); + + g_object_unref (out_of_gamut_color); } cairo_set_line_width (cr, 1.0); diff --git a/libgimpconfig/gimpcolorconfig.c b/libgimpconfig/gimpcolorconfig.c index 9717228376..a67c65c1d5 100644 --- a/libgimpconfig/gimpcolorconfig.c +++ b/libgimpconfig/gimpcolorconfig.c @@ -160,7 +160,7 @@ struct _GimpColorConfigPrivate gboolean simulation_use_black_point_compensation; gboolean simulation_optimize; gboolean simulation_gamut_check; - GimpRGB out_of_gamut_color; + GeglColor *out_of_gamut_color; gboolean show_rgb_u8; gboolean show_hsv; @@ -209,9 +209,13 @@ static void gimp_color_config_class_init (GimpColorConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GimpRGB color; + GeglColor *magenta; - gimp_rgba_set (&color, 1.0, 0.0, 1.0, 1.0); /* magenta */ + babl_init (); + + /* Magenta (sRGB space). */ + magenta = gegl_color_new (NULL); + gegl_color_set_rgba (magenta, 1.0, 0.0, 1.0, 1.0); object_class->finalize = gimp_color_config_finalize; object_class->set_property = gimp_color_config_set_property; @@ -318,12 +322,12 @@ gimp_color_config_class_init (GimpColorConfigClass *klass) FALSE, GIMP_PARAM_STATIC_STRINGS); - GIMP_CONFIG_PROP_RGB (object_class, PROP_OUT_OF_GAMUT_COLOR, - "out-of-gamut-color", - _("Out of gamut warning color"), - OUT_OF_GAMUT_COLOR_BLURB, - FALSE, &color, - GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_COLOR (object_class, PROP_OUT_OF_GAMUT_COLOR, + "out-of-gamut-color", + _("Out of gamut warning color"), + OUT_OF_GAMUT_COLOR_BLURB, + magenta, + GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_BOOLEAN (object_class, PROP_SHOW_RGB_U8, "show-rgb-u8", @@ -338,12 +342,20 @@ gimp_color_config_class_init (GimpColorConfigClass *klass) _("Show HSV instead of LCH"), FALSE, GIMP_PARAM_STATIC_STRINGS); + + g_object_unref (magenta); } static void gimp_color_config_init (GimpColorConfig *config) { + GeglColor *magenta = gegl_color_new (NULL); + config->priv = gimp_color_config_get_instance_private (config); + + /* Magenta (sRGB space). */ + gegl_color_set_rgba (magenta, 1.0, 0.0, 1.0, 1.0); + config->priv->out_of_gamut_color = magenta; } static void @@ -425,7 +437,8 @@ gimp_color_config_set_property (GObject *object, priv->simulation_gamut_check = g_value_get_boolean (value); break; case PROP_OUT_OF_GAMUT_COLOR: - priv->out_of_gamut_color = *(GimpRGB *) g_value_get_boxed (value); + g_clear_object (&priv->out_of_gamut_color); + priv->out_of_gamut_color = gegl_color_duplicate (g_value_get_object (value)); break; case PROP_SHOW_RGB_U8: priv->show_rgb_u8 = g_value_get_boolean (value); @@ -499,7 +512,7 @@ gimp_color_config_get_property (GObject *object, g_value_set_boolean (value, priv->simulation_gamut_check); break; case PROP_OUT_OF_GAMUT_COLOR: - g_value_set_boxed (value, &priv->out_of_gamut_color); + g_value_set_object (value, priv->out_of_gamut_color); break; case PROP_SHOW_RGB_U8: g_value_set_boolean (value, priv->show_rgb_u8); @@ -649,18 +662,17 @@ gimp_color_config_get_simulation_gamut_check (GimpColorConfig *config) /** * gimp_color_config_get_out_of_gamut_color: * @config: a #GimpColorConfig - * @color: return location for a #GimpRGB * + * Returns: (transfer full): the [class@Gegl.Color] to use to represent + * out-of-gamut pixels. * Since: 3.0 **/ -void -gimp_color_config_get_out_of_gamut_color (GimpColorConfig *config, - GimpRGB *color) +GeglColor * +gimp_color_config_get_out_of_gamut_color (GimpColorConfig *config) { - g_return_if_fail (GIMP_IS_COLOR_CONFIG (config)); - g_return_if_fail (color != NULL); + g_return_val_if_fail (GIMP_IS_COLOR_CONFIG (config), NULL); - *color = GET_PRIVATE (config)->out_of_gamut_color; + return gegl_color_duplicate (GET_PRIVATE (config)->out_of_gamut_color); } /** diff --git a/libgimpconfig/gimpcolorconfig.h b/libgimpconfig/gimpcolorconfig.h index 99a2ece72d..4f324fcd3c 100644 --- a/libgimpconfig/gimpcolorconfig.h +++ b/libgimpconfig/gimpcolorconfig.h @@ -75,8 +75,7 @@ GimpColorRenderingIntent gboolean gimp_color_config_get_simulation_bpc (GimpColorConfig *config); gboolean gimp_color_config_get_simulation_optimize (GimpColorConfig *config); gboolean gimp_color_config_get_simulation_gamut_check (GimpColorConfig *config); -void gimp_color_config_get_out_of_gamut_color (GimpColorConfig *config, - GimpRGB *color); +GeglColor * gimp_color_config_get_out_of_gamut_color (GimpColorConfig *config); GimpColorProfile * gimp_color_config_get_rgb_color_profile (GimpColorConfig *config, GError **error); diff --git a/libgimpconfig/gimpconfig.h b/libgimpconfig/gimpconfig.h index ae9f87fc3c..829ab5b4b4 100644 --- a/libgimpconfig/gimpconfig.h +++ b/libgimpconfig/gimpconfig.h @@ -19,6 +19,8 @@ #ifndef __GIMP_CONFIG_H__ #define __GIMP_CONFIG_H__ +#include + #define __GIMP_CONFIG_H_INSIDE__ #include diff --git a/libgimpwidgets/gimpcairo-utils.c b/libgimpwidgets/gimpcairo-utils.c index 0ed72ea13f..ed5cab4d9a 100644 --- a/libgimpwidgets/gimpcairo-utils.c +++ b/libgimpwidgets/gimpcairo-utils.c @@ -28,8 +28,10 @@ #include "libgimpbase/gimpbase.h" #include "libgimpcolor/gimpcolor.h" +#include "libgimpconfig/gimpconfig.h" #include "gimpcairo-utils.h" +#include "gimpwidgetsutils.h" /** @@ -200,3 +202,55 @@ gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf) return surface; } + +/** + * gimp_cairo_set_source_color: + * @cr: Cairo context. + * @color: the [class@Gegl.Color] to use as source pattern within @cr. + * @config: the color management settings. + * @softproof: whether the color must also be soft-proofed. + * @widget: (nullable): [class@Gtk.Widget] to draw the focus indicator on. + * + * Sets @color as the source pattern within @cr, taking into account the profile + * of the [class@Gdk.Monitor] which @widget is displayed on. + * + * If @config is set, the color configuration as set by the user will be used, + * in particular using any custom monitor profile set in preferences (overriding + * system-set profile). If no such custom profile is set, it will use the + * profile of the monitor @widget is displayed on and will default to sRGB if + * @widget is %NULL. + * + * Use [func@Gimp.get_color_configuration] to retrieve the user + * [class@Gimp.ColorConfig]. + * + * TODO: @softproof is currently unused. + * + * Since: 3.0 + **/ +void +gimp_cairo_set_source_color (cairo_t *cr, + GeglColor *color, + GimpColorConfig *config, + gboolean softproof, + GtkWidget *widget) +{ + GimpColorProfile *proof_profile = NULL; + GimpColorProfile *dest_profile = NULL; + const Babl *space = NULL; + gdouble rgba[4]; + + g_return_if_fail (GEGL_IS_COLOR (color)); + g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget)); + + _gimp_widget_get_profiles (widget, config, + softproof ? &proof_profile : NULL, + &dest_profile); + + if (dest_profile) + space = gimp_color_profile_get_space (dest_profile, + GIMP_COLOR_RENDERING_INTENT_RELATIVE_COLORIMETRIC, + NULL); + gegl_color_get_pixel (color, babl_format_with_space ("R'G'B'A double", space), rgba); + + cairo_set_source_rgba (cr, rgba[0], rgba[1], rgba[2], rgba[3]); +} diff --git a/libgimpwidgets/gimpcairo-utils.h b/libgimpwidgets/gimpcairo-utils.h index 85a5ee449c..0010b01baa 100644 --- a/libgimpwidgets/gimpcairo-utils.h +++ b/libgimpwidgets/gimpcairo-utils.h @@ -32,5 +32,11 @@ gboolean gimp_cairo_set_focus_line_pattern (cairo_t *cr, cairo_surface_t * gimp_cairo_surface_create_from_pixbuf (GdkPixbuf *pixbuf); +void gimp_cairo_set_source_color (cairo_t *cr, + GeglColor *color, + GimpColorConfig *config, + gboolean softproof, + GtkWidget *widget); + #endif /* __GIMP_CAIRO_UTILS_H__ */ diff --git a/libgimpwidgets/gimpcolorarea.c b/libgimpwidgets/gimpcolorarea.c index 841f2a41f0..c84a9a0522 100644 --- a/libgimpwidgets/gimpcolorarea.c +++ b/libgimpwidgets/gimpcolorarea.c @@ -464,18 +464,20 @@ gimp_color_area_draw (GtkWidget *widget, 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; + GeglColor *oog_color; + gint side = MIN (priv->width, priv->height) * 2 / 3; cairo_move_to (cr, priv->width, 0); cairo_line_to (cr, priv->width - side, 0); cairo_line_to (cr, priv->width, side); cairo_line_to (cr, priv->width, 0); - gimp_color_config_get_out_of_gamut_color (priv->config, &color); - gimp_cairo_set_source_rgb (cr, &color); + oog_color = gimp_color_config_get_out_of_gamut_color (priv->config); + gimp_cairo_set_source_color (cr, oog_color, priv->config, FALSE, widget); cairo_fill (cr); + + g_object_unref (oog_color); } if (priv->draw_border) diff --git a/libgimpwidgets/gimpcolorscale.c b/libgimpwidgets/gimpcolorscale.c index 9aeab7b8d9..aa617cb956 100644 --- a/libgimpwidgets/gimpcolorscale.c +++ b/libgimpwidgets/gimpcolorscale.c @@ -969,14 +969,16 @@ gimp_color_scale_notify_config (GimpColorConfig *config, GimpColorScale *scale) { GimpColorScalePrivate *priv = GET_PRIVATE (scale); - GimpRGB color; + GeglColor *color; gimp_color_scale_destroy_transform (scale); - gimp_color_config_get_out_of_gamut_color (config, &color); - gimp_rgb_get_uchar (&color, - priv->oog_color, - priv->oog_color + 1, - priv->oog_color + 2); + color = gimp_color_config_get_out_of_gamut_color (config); + /* TODO: shouldn't this be color-managed too, using the target space into + * consideration? + */ + gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), priv->oog_color); priv->needs_render = TRUE; + + g_object_unref (color); } diff --git a/libgimpwidgets/gimpcolorselect.c b/libgimpwidgets/gimpcolorselect.c index 8a21ad9daf..e91c5f2a3c 100644 --- a/libgimpwidgets/gimpcolorselect.c +++ b/libgimpwidgets/gimpcolorselect.c @@ -1981,15 +1981,17 @@ gimp_color_select_notify_config (GimpColorConfig *config, const GParamSpec *pspec, GimpColorSelect *select) { - GimpRGB color; + GeglColor *color; gimp_color_select_destroy_transform (select); - gimp_color_config_get_out_of_gamut_color (config, &color); - gimp_rgb_get_uchar (&color, - select->oog_color, - select->oog_color + 1, - select->oog_color + 2); + color = gimp_color_config_get_out_of_gamut_color (config); + /* TODO: shouldn't this be color-managed too, using the target space into + * consideration? + */ + gegl_color_get_pixel (color, babl_format ("R'G'B' u8"), select->oog_color); select->xy_needs_render = TRUE; select->z_needs_render = TRUE; + + g_object_unref (color); } diff --git a/libgimpwidgets/gimpwidgets.def b/libgimpwidgets/gimpwidgets.def index 668bcef979..7cc0a83c82 100644 --- a/libgimpwidgets/gimpwidgets.def +++ b/libgimpwidgets/gimpwidgets.def @@ -16,6 +16,7 @@ EXPORTS gimp_button_get_type gimp_button_new gimp_cairo_set_focus_line_pattern + gimp_cairo_set_source_color gimp_cairo_surface_create_from_pixbuf gimp_cell_renderer_color_get_type gimp_cell_renderer_color_new diff --git a/libgimpwidgets/gimpwidgetsutils.c b/libgimpwidgets/gimpwidgetsutils.c index bc32d8079d..147c1f23d6 100644 --- a/libgimpwidgets/gimpwidgetsutils.c +++ b/libgimpwidgets/gimpwidgetsutils.c @@ -1005,20 +1005,19 @@ gimp_widget_get_color_transform (GtkWidget *widget, if (gimp_color_config_get_simulation_gamut_check (config)) { - GimpRGB color; - cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0, }; - guchar r, g, b; + GeglColor *color; + cmsUInt16Number alarmCodes[cmsMAXCHANNELS] = { 0, }; flags |= GIMP_COLOR_TRANSFORM_FLAGS_GAMUT_CHECK; - gimp_color_config_get_out_of_gamut_color (config, &color); - gimp_rgb_get_uchar (&color, &r, &g, &b); - - alarmCodes[0] = (cmsUInt16Number) r * 256; - alarmCodes[1] = (cmsUInt16Number) g * 256; - alarmCodes[2] = (cmsUInt16Number) b * 256; - + color = gimp_color_config_get_out_of_gamut_color (config); + /* Little-CMS documentation doesn't say which space should the + * out-of-gamut color be. Let's just give sRGB data. + */ + gegl_color_get_pixel (color, babl_format ("R'G'B' u16"), alarmCodes); cmsSetAlarmCodes (alarmCodes); + + g_object_unref (color); } cache->transform = @@ -1107,6 +1106,35 @@ gimp_widget_set_native_handle (GtkWidget *widget, } +/* Internal functions */ + +void +_gimp_widget_get_profiles (GtkWidget *widget, + GimpColorConfig *config, + GimpColorProfile **proof_profile, + GimpColorProfile **dest_profile) +{ + g_return_if_fail (dest_profile != NULL && *dest_profile == NULL); + g_return_if_fail (proof_profile == NULL || *proof_profile == NULL); + + switch (gimp_color_config_get_mode (config)) + { + case GIMP_COLOR_MANAGEMENT_OFF: + return; + + case GIMP_COLOR_MANAGEMENT_SOFTPROOF: + if (proof_profile) + *proof_profile = gimp_color_config_get_simulation_color_profile (config, NULL); + + /* fallthru */ + + case GIMP_COLOR_MANAGEMENT_DISPLAY: + *dest_profile = get_display_profile (widget, config); + break; + } +} + + /* Private functions */ #ifdef GDK_WINDOWING_WAYLAND diff --git a/libgimpwidgets/gimpwidgetsutils.h b/libgimpwidgets/gimpwidgetsutils.h index 891bda5dd5..e715bd82d4 100644 --- a/libgimpwidgets/gimpwidgetsutils.h +++ b/libgimpwidgets/gimpwidgetsutils.h @@ -68,6 +68,15 @@ GimpColorTransform * gimp_widget_get_color_transform (GtkWidget *widget, void gimp_widget_set_native_handle (GtkWidget *widget, GBytes **handle); + +/* Internal use */ + +G_GNUC_INTERNAL void _gimp_widget_get_profiles (GtkWidget *widget, + GimpColorConfig *config, + GimpColorProfile **proof_profile, + GimpColorProfile **dest_profile); + + G_END_DECLS #endif /* __GIMP_WIDGETS_UTILS_H__ */