From e992ca3e514569a35b4077bb22315b0d085ff2b9 Mon Sep 17 00:00:00 2001 From: Jehan Date: Fri, 17 Nov 2023 22:36:31 +0100 Subject: [PATCH] app, libgimp, pdb, plug-ins: GimpText* using GeglColor. One of the big improvement in this commit is that text layers are now much better at space accuracy. They were already space-aware, yet rendered as sRGB u8 only before being converted to the image's space. It means that text layers had the following limitations: * Any color out of sRGB gamut were trimmed. * Precision was always 8-bit (even if the image was high-bit depth). Now GimpTextLayout keeps track of its source space (for RGB and CMYK only, this won't be as easy when we will support more backend, since Cairo has only RGB support for image data) and the image TRC (in case it bypasses the color space's TRB) and it draws within this gamut and space. It means first that we are not limited to sRGB colors; we will draw text main color in the full image gamut, with still 2 remaining limitations: * Unbounded colors are impossible because Pango format (to color text) uses hexadecimal (so even with half/float images, you can't draw out-of-gamut text unfortunately). * Main color precision is still 8-bit, yet a tiny bit better than before as we at least follow TRC (so we avoid some of the precision loss when converting, even though the bit-depth is still the biggest loss). The outline color on the other hand is drawn through Cairo API entirely, in float. This means that the outline color will now be without any precision loss. Note that this depends on CAIRO_FORMAT_RGBA128F which is only available since Cairo 1.17.2 which is not in Debian bookworm (our current baseline for GIMP 3.0). It means that the old precision will still happen with older Cairo version, as determined by #if code at compilation. --- app/core/gimpimage-color-profile.c | 23 ----- app/core/gimpimage-color-profile.h | 2 - app/core/gimpimage-private.h | 1 - app/display/gimpdisplayshell-dnd.c | 5 +- app/pdb/text-layer-cmds.c | 17 ++-- app/text/gimptext-vectors.c | 2 +- app/text/gimptext.c | 39 ++++---- app/text/gimptext.h | 4 +- app/text/gimptextlayer.c | 58 +++++++----- app/text/gimptextlayout.c | 137 ++++++++++++++++++++++++++--- app/text/gimptextlayout.h | 7 ++ app/tools/gimptextoptions.c | 49 ++++------- app/tools/gimptextoptions.h | 2 +- app/tools/gimptexttool.c | 2 +- app/widgets/gimptextstyleeditor.c | 6 +- app/widgets/gimpviewrenderer.c | 4 +- libgimp/gimplayer.c | 2 +- libgimp/gimptextlayer_pdb.c | 18 ++-- libgimp/gimptextlayer_pdb.h | 3 +- libgimpcolor/gimpcairo.c | 31 +++++-- libgimpcolor/gimpcairo.h | 3 +- meson.build | 3 + pdb/groups/text_layer.pdb | 4 +- plug-ins/common/file-pdf-save.c | 27 +++--- 24 files changed, 276 insertions(+), 173 deletions(-) diff --git a/app/core/gimpimage-color-profile.c b/app/core/gimpimage-color-profile.c index bcf8f2d604..56deec69f0 100644 --- a/app/core/gimpimage-color-profile.c +++ b/app/core/gimpimage-color-profile.c @@ -815,20 +815,6 @@ gimp_image_get_color_transform_to_srgb_u8 (GimpImage *image) return private->transform_to_srgb_u8; } -GimpColorTransform * -gimp_image_get_color_transform_from_srgb_u8 (GimpImage *image) -{ - GimpImagePrivate *private; - - g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); - - private = GIMP_IMAGE_GET_PRIVATE (image); - - gimp_image_create_color_transforms (image); - - return private->transform_from_srgb_u8; -} - GimpColorTransform * gimp_image_get_color_transform_to_srgb_double (GimpImage *image) { @@ -938,7 +924,6 @@ _gimp_image_free_color_transforms (GimpImage *image) GimpImagePrivate *private = GIMP_IMAGE_GET_PRIVATE (image); g_clear_object (&private->transform_to_srgb_u8); - g_clear_object (&private->transform_from_srgb_u8); g_clear_object (&private->transform_to_srgb_double); g_clear_object (&private->transform_from_srgb_double); @@ -1162,14 +1147,6 @@ gimp_image_create_color_transforms (GimpImage *image) GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, flags); - private->transform_from_srgb_u8 = - gimp_color_transform_new (srgb_profile, - babl_format ("R'G'B'A u8"), - private->color_profile, - gimp_image_get_layer_format (image, TRUE), - GIMP_COLOR_RENDERING_INTENT_PERCEPTUAL, - flags); - private->transform_to_srgb_double = gimp_color_transform_new (private->color_profile, gimp_image_get_layer_format (image, TRUE), diff --git a/app/core/gimpimage-color-profile.h b/app/core/gimpimage-color-profile.h index 607b9af531..1ad617217c 100644 --- a/app/core/gimpimage-color-profile.h +++ b/app/core/gimpimage-color-profile.h @@ -110,8 +110,6 @@ void gimp_image_import_color_profile (GimpImage *ima GimpColorTransform * gimp_image_get_color_transform_to_srgb_u8 (GimpImage *image); -GimpColorTransform * gimp_image_get_color_transform_from_srgb_u8 - (GimpImage *image); GimpColorTransform * gimp_image_get_color_transform_to_srgb_double (GimpImage *image); diff --git a/app/core/gimpimage-private.h b/app/core/gimpimage-private.h index b2f3d9448c..187d95ae5e 100644 --- a/app/core/gimpimage-private.h +++ b/app/core/gimpimage-private.h @@ -74,7 +74,6 @@ struct _GimpImagePrivate /* Cached color transforms: from layer to sRGB u8 and double, and back */ gboolean color_transforms_created; GimpColorTransform *transform_to_srgb_u8; - GimpColorTransform *transform_from_srgb_u8; GimpColorTransform *transform_to_srgb_double; GimpColorTransform *transform_from_srgb_double; diff --git a/app/display/gimpdisplayshell-dnd.c b/app/display/gimpdisplayshell-dnd.c index 89663fa717..d7aad98815 100644 --- a/app/display/gimpdisplayshell-dnd.c +++ b/app/display/gimpdisplayshell-dnd.c @@ -389,17 +389,14 @@ gimp_display_shell_dnd_fill (GimpDisplayShell *shell, gimp_fill_options_get_style (options) == GIMP_FILL_STYLE_BG_COLOR)) { GeglColor *color; - GimpRGB rgb; if (gimp_fill_options_get_style (options) == GIMP_FILL_STYLE_FG_COLOR) color = gimp_context_get_foreground (GIMP_CONTEXT (options)); else color = gimp_context_get_background (GIMP_CONTEXT (options)); - gegl_color_get_rgba_with_space (color, &rgb.r, &rgb.g, &rgb.b, &rgb.a, NULL); - gimp_text_layer_set (iter->data, NULL, - "color", &rgb, + "color", color, NULL); } else diff --git a/app/pdb/text-layer-cmds.c b/app/pdb/text-layer-cmds.c index fef0f974c1..9da53abd3d 100644 --- a/app/pdb/text-layer-cmds.c +++ b/app/pdb/text-layer-cmds.c @@ -734,20 +734,20 @@ text_layer_get_color_invoker (GimpProcedure *procedure, gboolean success = TRUE; GimpValueArray *return_vals; GimpTextLayer *layer; - GimpRGB color = { 0.0, 0.0, 0.0, 1.0 }; + GeglColor *color = NULL; layer = g_value_get_object (gimp_value_array_index (args, 0)); if (success) { - color = gimp_text_layer_get_text (layer)->color; + color = gegl_color_duplicate (gimp_text_layer_get_text (layer)->color); } return_vals = gimp_procedure_get_return_values (procedure, success, error ? *error : NULL); if (success) - gimp_value_set_rgb (gimp_value_array_index (return_vals, 1), &color); + g_value_take_object (gimp_value_array_index (return_vals, 1), color); return return_vals; } @@ -1685,12 +1685,11 @@ register_text_layer_procs (GimpPDB *pdb) FALSE, GIMP_PARAM_READWRITE)); gimp_procedure_add_return_value (procedure, - gimp_param_spec_rgb ("color", - "color", - "The color of the text.", - FALSE, - NULL, - GIMP_PARAM_READWRITE)); + gegl_param_spec_color ("color", + "color", + "The color of the text.", + NULL, + GIMP_PARAM_READWRITE)); gimp_pdb_register_procedure (pdb, procedure); g_object_unref (procedure); diff --git a/app/text/gimptext-vectors.c b/app/text/gimptext-vectors.c index 3a6901e9da..b6414cb507 100644 --- a/app/text/gimptext-vectors.c +++ b/app/text/gimptext-vectors.c @@ -82,7 +82,7 @@ gimp_text_vectors_new (GimpImage *image, gimp_image_get_resolution (image, &xres, &yres); - layout = gimp_text_layout_new (text, xres, yres, &error); + layout = gimp_text_layout_new (text, image, xres, yres, &error); if (error) { gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message); diff --git a/app/text/gimptext.c b/app/text/gimptext.c index 7e72fecbd6..e4d607e4a9 100644 --- a/app/text/gimptext.c +++ b/app/text/gimptext.c @@ -141,8 +141,8 @@ gimp_text_class_init (GimpTextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass); - GimpRGB black; - GimpRGB gray; + GeglColor *black = gegl_color_new ("black"); + GeglColor *gray = gegl_color_new ("gray"); GimpMatrix2 identity; gchar *language; GParamSpec *array_spec; @@ -162,8 +162,6 @@ gimp_text_class_init (GimpTextClass *klass) gimp_object_class->get_memsize = gimp_text_get_memsize; - gimp_rgba_set (&black, 0.0, 0.0, 0.0, GIMP_OPACITY_OPAQUE); - gimp_rgba_set (&gray, 0.75, 0.75, 0.75, GIMP_OPACITY_OPAQUE); gimp_matrix2_identity (&identity); GIMP_CONFIG_PROP_STRING (object_class, PROP_TEXT, @@ -235,11 +233,11 @@ gimp_text_class_init (GimpTextClass *klass) GIMP_TEXT_DIRECTION_LTR, GIMP_PARAM_STATIC_STRINGS); - GIMP_CONFIG_PROP_RGB (object_class, PROP_COLOR, - "color", - NULL, NULL, - FALSE, &black, - GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_COLOR (object_class, PROP_COLOR, + "color", + NULL, NULL, + black, + GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_ENUM (object_class, PROP_OUTLINE, "outline", @@ -341,10 +339,10 @@ gimp_text_class_init (GimpTextClass *klass) "outline-pattern", NULL, NULL, GIMP_TYPE_PATTERN, GIMP_PARAM_STATIC_STRINGS); - GIMP_CONFIG_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND, - "outline-foreground", NULL, NULL, - FALSE, &gray, - GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_COLOR (object_class, PROP_OUTLINE_FOREGROUND, + "outline-foreground", NULL, NULL, + gray, + GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_DOUBLE (object_class, PROP_OUTLINE_WIDTH, "outline-width", NULL, NULL, 0.0, 8192.0, 4.0, @@ -394,6 +392,8 @@ gimp_text_class_init (GimpTextClass *klass) GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + g_object_unref (black); + g_object_unref (gray); } static void @@ -417,6 +417,8 @@ gimp_text_finalize (GObject *object) g_clear_pointer (&text->markup, g_free); g_clear_pointer (&text->language, g_free); g_clear_object (&text->font); + g_clear_object (&text->color); + g_clear_object (&text->outline_foreground); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -462,7 +464,7 @@ gimp_text_get_property (GObject *object, g_value_set_string (value, text->language); break; case PROP_COLOR: - g_value_set_boxed (value, &text->color); + g_value_set_object (value, text->color); break; case PROP_OUTLINE: g_value_set_enum (value, text->outline); @@ -504,7 +506,7 @@ gimp_text_get_property (GObject *object, g_value_set_enum (value, text->outline_style); break; case PROP_OUTLINE_FOREGROUND: - g_value_set_boxed (value, &text->outline_foreground); + g_value_set_object (value, text->outline_foreground); break; case PROP_OUTLINE_PATTERN: g_value_set_object (value, text->outline_pattern); @@ -555,7 +557,6 @@ gimp_text_set_property (GObject *object, GParamSpec *pspec) { GimpText *text = GIMP_TEXT (object); - GimpRGB *color; GimpMatrix2 *matrix; switch (property_id) @@ -609,8 +610,7 @@ gimp_text_set_property (GObject *object, text->base_dir = g_value_get_enum (value); break; case PROP_COLOR: - color = g_value_get_boxed (value); - text->color = *color; + g_set_object (&text->color, g_value_get_object (value));; break; case PROP_OUTLINE: text->outline = g_value_get_enum (value); @@ -653,8 +653,7 @@ gimp_text_set_property (GObject *object, text->outline_style = g_value_get_enum (value); break; case PROP_OUTLINE_FOREGROUND: - color = g_value_get_boxed (value); - text->outline_foreground = *color; + g_set_object (&text->outline_foreground, g_value_get_object (value));; break; case PROP_OUTLINE_PATTERN: { diff --git a/app/text/gimptext.h b/app/text/gimptext.h index 6b6434edd1..c013a949a0 100644 --- a/app/text/gimptext.h +++ b/app/text/gimptext.h @@ -49,10 +49,10 @@ struct _GimpText gboolean kerning; gchar *language; GimpTextDirection base_dir; - GimpRGB color; + GeglColor *color; GimpCustomStyle outline_style; GimpPattern *outline_pattern; - GimpRGB outline_foreground; + GeglColor *outline_foreground; gdouble outline_width; GimpCapStyle outline_cap_style; GimpJoinStyle outline_join_style; diff --git a/app/text/gimptextlayer.c b/app/text/gimptextlayer.c index a5d728f0df..82af355a31 100644 --- a/app/text/gimptextlayer.c +++ b/app/text/gimptextlayer.c @@ -53,7 +53,6 @@ #include "gimptext.h" #include "gimptextlayer.h" -#include "gimptextlayer-transform.h" #include "gimptextlayout.h" #include "gimptextlayout-render.h" @@ -725,7 +724,7 @@ gimp_text_layer_render (GimpTextLayer *layer) gimp_image_get_resolution (image, &xres, &yres); - layout = gimp_text_layout_new (layer->text, xres, yres, &error); + layout = gimp_text_layout_new (layer->text, image, xres, yres, &error); if (error) { gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message); @@ -927,9 +926,8 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, { GimpDrawable *drawable = GIMP_DRAWABLE (layer); GimpItem *item = GIMP_ITEM (layer); - GimpImage *image = gimp_item_get_image (item); + const Babl *format; GeglBuffer *buffer; - GimpColorTransform *transform; cairo_t *cr; cairo_surface_t *surface; gint width; @@ -941,7 +939,11 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, width = gimp_item_get_width (item); height = gimp_item_get_height (item); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2) + surface = cairo_image_surface_create (CAIRO_FORMAT_RGBA128F, width, height); +#else surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); +#endif status = cairo_surface_status (surface); if (status != CAIRO_STATUS_SUCCESS) @@ -968,7 +970,6 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, if (layer->text->outline != GIMP_TEXT_OUTLINE_NONE) { GimpText *text = layer->text; - GimpRGB col = text->outline_foreground; cairo_save (cr); @@ -998,7 +999,16 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, } else { - cairo_set_source_rgba (cr, col.r, col.g, col.b, col.a); + GeglColor *col = text->outline_foreground; + gdouble color[3]; + + format = gimp_text_layout_get_format (layout, "double"); + gegl_color_get_pixel (col, format, color); + /* Text layout can be either grayscale or RGB without alpha. */ + if (! babl_space_is_gray (babl_format_get_space (format))) + cairo_set_source_rgba (cr, color[0], color[1], color[2], 1.0); + else + cairo_set_source_rgba (cr, color[0], color[0], color[0], 1.0); } cairo_set_line_width (cr, text->outline_width * 2); @@ -1014,23 +1024,29 @@ gimp_text_layer_render_layout (GimpTextLayer *layer, cairo_surface_flush (surface); - buffer = gimp_cairo_surface_create_buffer (surface); - - transform = gimp_image_get_color_transform_from_srgb_u8 (image); - - if (transform) +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2) + /* The CAIRO_FORMAT_RGBA128F surface maps to the layout TRC and space. */ + switch (gimp_text_layout_get_trc (layout)) { - gimp_color_transform_process_buffer (transform, - buffer, - NULL, - gimp_drawable_get_buffer (drawable), - NULL); - } - else - { - gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, - gimp_drawable_get_buffer (drawable), NULL); + case GIMP_TRC_LINEAR: + format = babl_format_with_space ("RGBA float", gimp_text_layout_get_space (layout)); + break; + case GIMP_TRC_NON_LINEAR: + format = babl_format_with_space ("R'G'B'A float", gimp_text_layout_get_space (layout)); + break; + case GIMP_TRC_PERCEPTUAL: + format = babl_format_with_space ("R~G~B~A float", gimp_text_layout_get_space (layout)); + break; + default: + g_return_if_reached (); } +#else + format = babl_format_with_space ("cairo-ARGB32", gimp_text_layout_get_space (layout)); +#endif + buffer = gimp_cairo_surface_create_buffer (surface, format); + + gimp_gegl_buffer_copy (buffer, NULL, GEGL_ABYSS_NONE, + gimp_drawable_get_buffer (drawable), NULL); g_object_unref (buffer); cairo_surface_destroy (surface); diff --git a/app/text/gimptextlayout.c b/app/text/gimptextlayout.c index 50cd670d8d..7869e38327 100644 --- a/app/text/gimptextlayout.c +++ b/app/text/gimptextlayout.c @@ -32,7 +32,10 @@ #include "text-types.h" +#include "gegl/gimp-babl.h" + #include "core/gimperror.h" +#include "core/gimpimage.h" #include "gimpfont.h" @@ -50,6 +53,8 @@ struct _GimpTextLayout gdouble yres; PangoLayout *layout; PangoRectangle extents; + const Babl *layout_space; + GimpTRCType layout_trc; }; @@ -105,18 +110,21 @@ gimp_text_layout_finalize (GObject *object) GimpTextLayout * -gimp_text_layout_new (GimpText *text, - gdouble xres, - gdouble yres, - GError **error) +gimp_text_layout_new (GimpText *text, + GimpImage *target_image, + gdouble xres, + gdouble yres, + GError **error) { GimpTextLayout *layout; + const Babl *target_space; PangoContext *context; PangoFontDescription *font_desc; PangoAlignment alignment = PANGO_ALIGN_LEFT; gint size; g_return_val_if_fail (GIMP_IS_TEXT (text), NULL); + g_return_val_if_fail (GIMP_IS_IMAGE (target_image), NULL); font_desc = pango_font_description_from_string (gimp_font_get_lookup_name (text->font)); g_return_val_if_fail (font_desc != NULL, NULL); @@ -141,6 +149,25 @@ gimp_text_layout_new (GimpText *text, pango_layout_set_font_description (layout->layout, font_desc); pango_font_description_free (font_desc); + target_space = gimp_image_get_layer_space (target_image); +#if BABL_MINOR_VERSION > 1 || (BABL_MINOR_VERSION == 1 && BABL_MICRO_VERSION >= 107) + if (babl_space_is_rgb (target_space) || babl_space_is_gray (target_space)) +#else + if (! babl_space_is_cmyk (target_space)) +#endif + layout->layout_space = target_space; + else + layout->layout_space = NULL; + +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2) + layout->layout_trc = gimp_babl_trc (gimp_image_get_precision (target_image)); +#else + /* With older Cairo, we just use cairo-ARGB32 with no linear or perceptual + * option. + */ + layout->layout_trc = GIMP_TRC_NON_LINEAR; +#endif + gimp_text_layout_set_markup (layout, error); switch (text->justify) @@ -221,6 +248,72 @@ gimp_text_layout_new (GimpText *text, return layout; } +const GimpTRCType +gimp_text_layout_get_trc (GimpTextLayout *layout) +{ + g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), GIMP_TRC_NON_LINEAR); + + return layout->layout_trc; +} + +const Babl * +gimp_text_layout_get_format (GimpTextLayout *layout, + const gchar *babl_type) +{ + const Babl *format; + gchar *format_name; + + g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), NULL); + + if (! babl_space_is_gray (layout->layout_space)) + { + switch (layout->layout_trc) + { + case GIMP_TRC_LINEAR: + format_name = g_strdup_printf ("RGB %s", babl_type); + break; + case GIMP_TRC_NON_LINEAR: + format_name = g_strdup_printf ("R'G'B' %s", babl_type); + break; + case GIMP_TRC_PERCEPTUAL: + format_name = g_strdup_printf ("R~G~B~ %s", babl_type); + break; + default: + g_return_val_if_reached (NULL); + } + } + else + { + switch (layout->layout_trc) + { + case GIMP_TRC_LINEAR: + format_name = g_strdup_printf ("Y %s", babl_type); + break; + case GIMP_TRC_NON_LINEAR: + format_name = g_strdup_printf ("Y' %s", babl_type); + break; + case GIMP_TRC_PERCEPTUAL: + format_name = g_strdup_printf ("Y~ %s", babl_type); + break; + default: + g_return_val_if_reached (NULL); + } + } + + format = babl_format_with_space (format_name, layout->layout_space); + g_free (format_name); + + return format; +} + +const Babl * +gimp_text_layout_get_space (GimpTextLayout *layout) +{ + g_return_val_if_fail (GIMP_IS_TEXT_LAYOUT (layout), NULL); + + return layout->layout_space; +} + gboolean gimp_text_layout_get_size (GimpTextLayout *layout, gint *width, @@ -499,17 +592,31 @@ static gchar * gimp_text_layout_apply_tags (GimpTextLayout *layout, const gchar *markup) { - GimpText *text = layout->text; - gchar *result; - - { - guchar r, g, b; - - gimp_rgb_get_uchar (&text->color, &r, &g, &b); + const Babl *format; + GimpText *text = layout->text; + gchar *result; + guchar color[3]; + /* Unfortunately Pango markup are very limited, color-wise. Colors are + * written in hexadecimal, so they are u8 as maximum precision, and + * unbounded colors are not accessible. + * At the very least, what we do is to write color values in the target + * space in the PangoLayout, so that we don't end up stuck to sRGB text + * colors even in images with wider gamut spaces. + * + * Moreover this is limited to RGB and Grayscale spaces. Therefore, images + * with other backends will be limited to the sRGB gamut, for as long as Pango + * do not evolve (or unless we changed our rendering backend). + */ + format = gimp_text_layout_get_format (layout, "u8"); + gegl_color_get_pixel (text->color, format, color); + if (! babl_space_is_gray (babl_format_get_space (format))) result = g_strdup_printf ("%s", - r, g, b, markup); - } + color[0], color[1], color[2], markup); + else + result = g_strdup_printf ("%s", + color[0], color[0], color[0], markup); + /* Updating font 'locl' (if supported) with 'lang' feature tag */ if (text->language) { @@ -598,7 +705,9 @@ gimp_text_layout_set_markup (GimpTextLayout *layout, } } else - pango_layout_set_markup (layout->layout, markup, -1); + { + pango_layout_set_markup (layout->layout, markup, -1); + } g_free (markup); } diff --git a/app/text/gimptextlayout.h b/app/text/gimptextlayout.h index 042751b21d..ed7fd89ed8 100644 --- a/app/text/gimptextlayout.h +++ b/app/text/gimptextlayout.h @@ -38,9 +38,16 @@ struct _GimpTextLayoutClass GType gimp_text_layout_get_type (void) G_GNUC_CONST; GimpTextLayout * gimp_text_layout_new (GimpText *text, + GimpImage *target_image, gdouble xres, gdouble yres, GError **error); + +const Babl * gimp_text_layout_get_space (GimpTextLayout *layout); +const GimpTRCType gimp_text_layout_get_trc (GimpTextLayout *layout); +const Babl * gimp_text_layout_get_format (GimpTextLayout *layout, + const gchar *babl_type); + gboolean gimp_text_layout_get_size (GimpTextLayout *layout, gint *width, gint *height); diff --git a/app/tools/gimptextoptions.c b/app/tools/gimptextoptions.c index 15879b7f92..4ca43a6747 100644 --- a/app/tools/gimptextoptions.c +++ b/app/tools/gimptextoptions.c @@ -152,10 +152,9 @@ static void gimp_text_options_class_init (GimpTextOptionsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); - GimpRGB gray; + GeglColor *gray = gegl_color_new ("gray"); GParamSpec *array_spec; - gimp_rgba_set (&gray, 0.75, 0.75, 0.75, GIMP_OPACITY_OPAQUE); object_class->finalize = gimp_text_options_finalize; object_class->set_property = gimp_text_options_set_property; object_class->get_property = gimp_text_options_get_property; @@ -286,11 +285,11 @@ gimp_text_options_class_init (GimpTextOptionsClass *klass) GIMP_TYPE_CUSTOM_STYLE, GIMP_CUSTOM_STYLE_SOLID_COLOR, GIMP_PARAM_STATIC_STRINGS); - GIMP_CONFIG_PROP_RGB (object_class, PROP_OUTLINE_FOREGROUND, - "outline-foreground", - NULL, NULL, - FALSE, &gray, - GIMP_PARAM_STATIC_STRINGS); + GIMP_CONFIG_PROP_COLOR (object_class, PROP_OUTLINE_FOREGROUND, + "outline-foreground", + NULL, NULL, + gray, + GIMP_PARAM_STATIC_STRINGS); GIMP_CONFIG_PROP_OBJECT (object_class, PROP_OUTLINE_PATTERN, "outline-pattern", NULL, NULL, @@ -349,6 +348,8 @@ gimp_text_options_class_init (GimpTextOptionsClass *klass) array_spec, GIMP_PARAM_STATIC_STRINGS | GIMP_CONFIG_PARAM_FLAGS)); + + g_object_unref (gray); } static void @@ -365,7 +366,10 @@ gimp_text_options_config_iface_init (GimpConfigInterface *config_iface) static void gimp_text_options_init (GimpTextOptions *options) { - options->size_entry = NULL; + GeglColor *gray = gegl_color_new ("gray"); + + options->size_entry = NULL; + options->outline_foreground = gray; } static void @@ -374,6 +378,7 @@ gimp_text_options_finalize (GObject *object) GimpTextOptions *options = GIMP_TEXT_OPTIONS (object); g_clear_pointer (&options->language, g_free); + g_clear_object (&options->outline_foreground); G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -429,7 +434,7 @@ gimp_text_options_get_property (GObject *object, g_value_set_enum (value, options->outline_style); break; case PROP_OUTLINE_FOREGROUND: - g_value_set_boxed (value, &options->outline_foreground); + g_value_set_object (value, options->outline_foreground); break; case PROP_OUTLINE_PATTERN: g_value_set_object (value, options->outline_pattern); @@ -491,7 +496,6 @@ gimp_text_options_set_property (GObject *object, GParamSpec *pspec) { GimpTextOptions *options = GIMP_TEXT_OPTIONS (object); - GimpRGB *color; switch (property_id) { @@ -537,8 +541,7 @@ gimp_text_options_set_property (GObject *object, options->outline_style = g_value_get_enum (value); break; case PROP_OUTLINE_FOREGROUND: - color = g_value_get_boxed (value); - options->outline_foreground = *color; + g_set_object (&options->outline_foreground, g_value_get_object (value));; break; case PROP_OUTLINE_PATTERN: { @@ -683,17 +686,11 @@ gimp_text_options_notify_color (GimpContext *context, GParamSpec *pspec, GimpText *text) { - GeglColor *color; - GimpRGB rgb; - - color = gimp_context_get_foreground (context); - gegl_color_get_pixel (color, babl_format ("R'G'B'A double"), &rgb); - g_signal_handlers_block_by_func (text, gimp_text_options_notify_text_color, context); - g_object_set (text, "color", &rgb, NULL); + g_object_set (text, "color", gimp_context_get_foreground (context), NULL); g_signal_handlers_unblock_by_func (text, gimp_text_options_notify_text_color, @@ -705,18 +702,13 @@ gimp_text_options_notify_text_color (GimpText *text, GParamSpec *pspec, GimpContext *context) { - GeglColor *color = gegl_color_new ("black"); - g_signal_handlers_block_by_func (context, gimp_text_options_notify_color, text); - gegl_color_set_rgba_with_space (color, text->color.r, text->color.g, text->color.b, text->color.a, NULL); - gimp_context_set_foreground (context, color); + gimp_context_set_foreground (context, text->color); g_signal_handlers_unblock_by_func (context, gimp_text_options_notify_color, text); - - g_object_unref (color); } /* This function could live in gimptexttool.c also. @@ -727,21 +719,16 @@ gimp_text_options_connect_text (GimpTextOptions *options, GimpText *text) { GimpContext *context; - GeglColor *color; - GimpRGB rgb; g_return_if_fail (GIMP_IS_TEXT_OPTIONS (options)); g_return_if_fail (GIMP_IS_TEXT (text)); context = GIMP_CONTEXT (options); - color = gimp_context_get_foreground (context); - gegl_color_get_pixel (color, babl_format ("R'G'B'A double"), &rgb); - gimp_config_sync (G_OBJECT (options), G_OBJECT (text), 0); g_object_set (text, - "color", &rgb, + "color", gimp_context_get_foreground (context), "font", gimp_context_get_font (context), NULL); diff --git a/app/tools/gimptextoptions.h b/app/tools/gimptextoptions.h index d004fcd35e..c1a6cc10d0 100644 --- a/app/tools/gimptextoptions.h +++ b/app/tools/gimptextoptions.h @@ -51,7 +51,7 @@ struct _GimpTextOptions GimpTextOutline outline; GimpCustomStyle outline_style; - GimpRGB outline_foreground; + GeglColor *outline_foreground; GimpPattern *outline_pattern; gdouble outline_width; GimpUnit outline_unit; diff --git a/app/tools/gimptexttool.c b/app/tools/gimptexttool.c index 2e49bfc6a9..f98a62e95e 100644 --- a/app/tools/gimptexttool.c +++ b/app/tools/gimptexttool.c @@ -2031,7 +2031,7 @@ gimp_text_tool_ensure_layout (GimpTextTool *text_tool) gimp_image_get_resolution (image, &xres, &yres); text_tool->layout = gimp_text_layout_new (text_tool->layer->text, - xres, yres, &error); + image, xres, yres, &error); if (error) { gimp_message_literal (image->gimp, NULL, GIMP_MESSAGE_ERROR, error->message); diff --git a/app/widgets/gimptextstyleeditor.c b/app/widgets/gimptextstyleeditor.c index 68062227b1..bece227ce5 100644 --- a/app/widgets/gimptextstyleeditor.c +++ b/app/widgets/gimptextstyleeditor.c @@ -781,12 +781,14 @@ gimp_text_style_editor_set_color (GimpTextStyleEditor *editor, static void gimp_text_style_editor_set_default_color (GimpTextStyleEditor *editor) { + GimpRGB rgb; + g_signal_handlers_block_by_func (editor->color_button, gimp_text_style_editor_color_changed, editor); - gimp_color_button_set_color (GIMP_COLOR_BUTTON (editor->color_button), - &editor->text->color); + gegl_color_get_pixel (editor->text->color, babl_format ("R'G'B'A double"), &rgb); + gimp_color_button_set_color (GIMP_COLOR_BUTTON (editor->color_button), &rgb); g_signal_handlers_unblock_by_func (editor->color_button, gimp_text_style_editor_color_changed, diff --git a/app/widgets/gimpviewrenderer.c b/app/widgets/gimpviewrenderer.c index f5214e7bbb..18e128e4df 100644 --- a/app/widgets/gimpviewrenderer.c +++ b/app/widgets/gimpviewrenderer.c @@ -1209,7 +1209,7 @@ gimp_view_render_temp_buf_to_surface (GimpViewRenderer *renderer, width, height); src_buffer = gimp_temp_buf_create_buffer (temp_buf); - dest_buffer = gimp_cairo_surface_create_buffer (alpha_surface); + dest_buffer = gimp_cairo_surface_create_buffer (alpha_surface, NULL); transform = gimp_view_renderer_get_color_transform (renderer, widget, @@ -1258,7 +1258,7 @@ gimp_view_render_temp_buf_to_surface (GimpViewRenderer *renderer, cairo_surface_flush (surface); src_buffer = gimp_temp_buf_create_buffer (temp_buf); - dest_buffer = gimp_cairo_surface_create_buffer (surface); + dest_buffer = gimp_cairo_surface_create_buffer (surface, NULL); transform = gimp_view_renderer_get_color_transform (renderer, widget, diff --git a/libgimp/gimplayer.c b/libgimp/gimplayer.c index 86275235c1..a6f3ccbc15 100644 --- a/libgimp/gimplayer.c +++ b/libgimp/gimplayer.c @@ -285,7 +285,7 @@ gimp_layer_new_from_surface (GimpImage *image, if (layer == NULL) return NULL; - src_buffer = gimp_cairo_surface_create_buffer (surface); + src_buffer = gimp_cairo_surface_create_buffer (surface, NULL); dest_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)); gegl_buffer_copy (src_buffer, NULL, GEGL_ABYSS_NONE, diff --git a/libgimp/gimptextlayer_pdb.c b/libgimp/gimptextlayer_pdb.c index a40a8a8f93..eb3416e21d 100644 --- a/libgimp/gimptextlayer_pdb.c +++ b/libgimp/gimptextlayer_pdb.c @@ -869,23 +869,21 @@ gimp_text_layer_set_justification (GimpTextLayer *layer, /** * gimp_text_layer_get_color: * @layer: The text layer. - * @color: (out caller-allocates): The color of the text. * * Get the color of the text in a text layer. * * This procedure returns the color of the text in a text layer. * - * Returns: TRUE on success. + * Returns: (transfer full): The color of the text. * * Since: 2.6 **/ -gboolean -gimp_text_layer_get_color (GimpTextLayer *layer, - GimpRGB *color) +GeglColor * +gimp_text_layer_get_color (GimpTextLayer *layer) { GimpValueArray *args; GimpValueArray *return_vals; - gboolean success = TRUE; + GeglColor *color = NULL; args = gimp_value_array_new_from_types (NULL, GIMP_TYPE_TEXT_LAYER, layer, @@ -896,14 +894,12 @@ gimp_text_layer_get_color (GimpTextLayer *layer, args); gimp_value_array_unref (args); - success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS; - - if (success) - GIMP_VALUES_GET_RGB (return_vals, 1, &*color); + if (GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS) + color = g_value_dup_object (gimp_value_array_index (return_vals, 1)); gimp_value_array_unref (return_vals); - return success; + return color; } /** diff --git a/libgimp/gimptextlayer_pdb.h b/libgimp/gimptextlayer_pdb.h index a5c0a88480..15a80a6a0a 100644 --- a/libgimp/gimptextlayer_pdb.h +++ b/libgimp/gimptextlayer_pdb.h @@ -69,8 +69,7 @@ gboolean gimp_text_layer_set_base_direction (GimpTextLayer GimpTextJustification gimp_text_layer_get_justification (GimpTextLayer *layer); gboolean gimp_text_layer_set_justification (GimpTextLayer *layer, GimpTextJustification justify); -gboolean gimp_text_layer_get_color (GimpTextLayer *layer, - GimpRGB *color); +GeglColor* gimp_text_layer_get_color (GimpTextLayer *layer); gboolean gimp_text_layer_set_color (GimpTextLayer *layer, const GimpRGB *color); gdouble gimp_text_layer_get_indent (GimpTextLayer *layer); diff --git a/libgimpcolor/gimpcairo.c b/libgimpcolor/gimpcairo.c index 756b62fcf8..850ad22bbf 100644 --- a/libgimpcolor/gimpcairo.c +++ b/libgimpcolor/gimpcairo.c @@ -162,9 +162,14 @@ gimp_cairo_surface_get_format (cairo_surface_t *surface) switch (cairo_image_surface_get_format (surface)) { - case CAIRO_FORMAT_RGB24: return babl_format ("cairo-RGB24"); - case CAIRO_FORMAT_ARGB32: return babl_format ("cairo-ARGB32"); - case CAIRO_FORMAT_A8: return babl_format ("cairo-A8"); + case CAIRO_FORMAT_RGB24: return babl_format ("cairo-RGB24"); + case CAIRO_FORMAT_ARGB32: return babl_format ("cairo-ARGB32"); + case CAIRO_FORMAT_A8: return babl_format ("cairo-A8"); +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 17, 2) + /* Since Cairo 1.17.2 */ + case CAIRO_FORMAT_RGB96F: return babl_format ("R'B'B' float"); + case CAIRO_FORMAT_RGBA128F: return babl_format ("R'G'B'A float"); +#endif default: break; @@ -176,27 +181,37 @@ gimp_cairo_surface_get_format (cairo_surface_t *surface) /** * gimp_cairo_surface_create_buffer: * @surface: a Cairo surface + * @format: a Babl format. * * This function returns a #GeglBuffer which wraps @surface's pixels. * It must only be called on image surfaces, calling it on other surface * types is an error. * + * If @format is set, the returned [class@Gegl.Buffer] will use it. It has to + * map with @surface Cairo format. If unset, the buffer format will be + * determined from @surface. The main difference is that automatically + * determined format has sRGB space and TRC by default. + * * Returns: (transfer full): a #GeglBuffer * * Since: 2.10 **/ GeglBuffer * -gimp_cairo_surface_create_buffer (cairo_surface_t *surface) +gimp_cairo_surface_create_buffer (cairo_surface_t *surface, + const Babl *format) { - const Babl *format; - gint width; - gint height; + gint width; + gint height; g_return_val_if_fail (surface != NULL, NULL); g_return_val_if_fail (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_IMAGE, NULL); + g_return_val_if_fail (format == NULL || + babl_format_get_bytes_per_pixel (format) == babl_format_get_bytes_per_pixel (gimp_cairo_surface_get_format (surface)), + NULL); - format = gimp_cairo_surface_get_format (surface); + if (format == NULL) + format = gimp_cairo_surface_get_format (surface); width = cairo_image_surface_get_width (surface); height = cairo_image_surface_get_height (surface); diff --git a/libgimpcolor/gimpcairo.h b/libgimpcolor/gimpcairo.h index d96e963aad..a88ab5a496 100644 --- a/libgimpcolor/gimpcairo.h +++ b/libgimpcolor/gimpcairo.h @@ -35,7 +35,8 @@ cairo_pattern_t * gimp_cairo_checkerboard_create (cairo_t *cr, const GimpRGB *dark); const Babl * gimp_cairo_surface_get_format (cairo_surface_t *surface); -GeglBuffer * gimp_cairo_surface_create_buffer (cairo_surface_t *surface); +GeglBuffer * gimp_cairo_surface_create_buffer (cairo_surface_t *surface, + const Babl *format); /* some useful macros for writing directly to a Cairo surface */ diff --git a/meson.build b/meson.build index 6f4e0172e3..fb22530dcc 100644 --- a/meson.build +++ b/meson.build @@ -351,6 +351,9 @@ if not babl.found() # because it would be a newer version. babl = dependency('babl', version: '>='+babl_minver) endif +# TODO: we want to bump to Cairo 1.17.2 when possible in order to use +# CAIRO_FORMAT_RGBA128F unconditionally. At time of writing, it's not possible +# because of our bookworm availability requirement. cairo_minver = '1.14.0' cairo = dependency('cairo', version: '>='+cairo_minver) diff --git a/pdb/groups/text_layer.pdb b/pdb/groups/text_layer.pdb index 7ce06bb692..6b6f639d75 100644 --- a/pdb/groups/text_layer.pdb +++ b/pdb/groups/text_layer.pdb @@ -732,14 +732,14 @@ HELP ); @outargs = ( - { name => 'color', type => 'color', void_ret => 1, + { name => 'color', type => 'geglcolor', desc => 'The color of the text.' } ); %invoke = ( code => <<'CODE' { - color = gimp_text_layer_get_text (layer)->color; + color = gegl_color_duplicate (gimp_text_layer_get_text (layer)->color); } CODE ); diff --git a/plug-ins/common/file-pdf-save.c b/plug-ins/common/file-pdf-save.c index ffa72aacd0..9753357bc1 100644 --- a/plug-ins/common/file-pdf-save.c +++ b/plug-ins/common/file-pdf-save.c @@ -1438,7 +1438,7 @@ get_cairo_surface (GimpDrawable *drawable, return NULL; } - dest_buffer = gimp_cairo_surface_create_buffer (surface); + dest_buffer = gimp_cairo_surface_create_buffer (surface, NULL); if (as_mask) { /* src_buffer represents a mask in "Y u8", "Y u16", etc. formats. @@ -1555,7 +1555,8 @@ drawText (GimpLayer *layer, cairo_font_options_t *options; gint x; gint y; - GimpRGB rgb; + GeglColor *color; + gdouble rgb[3]; GimpUnit unit; gdouble size; GimpTextHintStyle hinting; @@ -1588,20 +1589,18 @@ drawText (GimpLayer *layer, /* When dealing with a gray/indexed image, the viewed color of the text layer * can be different than the one kept in the memory */ if (type == GIMP_RGBA_IMAGE) - { - gimp_text_layer_get_color (GIMP_TEXT_LAYER (layer), &rgb); - } + color = gimp_text_layer_get_color (GIMP_TEXT_LAYER (layer)); else - { - GeglColor *color; + gimp_image_pick_color (gimp_item_get_image (GIMP_ITEM (layer)), 1, + (const GimpItem**) &layer, x, y, FALSE, FALSE, 0, + &color); - gimp_image_pick_color (gimp_item_get_image (GIMP_ITEM (layer)), 1, - (const GimpItem**) &layer, x, y, FALSE, FALSE, 0, - &color); - gegl_color_get_rgba_with_space (color, &rgb.r, &rgb.g, &rgb.b, &rgb.a, NULL); - } - - cairo_set_source_rgba (cr, rgb.r, rgb.g, rgb.b, opacity); + /* TODO: this export plug-in is not space-aware yet, so we draw everything as + * sRGB for the time being. + */ + gegl_color_get_pixel (color, babl_format_with_space ("R'G'B' double", NULL), rgb); + cairo_set_source_rgba (cr, rgb[0], rgb[1], rgb[2], opacity); + g_object_unref (color); /* Hinting */ hinting = gimp_text_layer_get_hint_style (GIMP_TEXT_LAYER (layer));