diff --git a/app/core/gimpdata.c b/app/core/gimpdata.c index f764f879a8..d6c0c52192 100644 --- a/app/core/gimpdata.c +++ b/app/core/gimpdata.c @@ -175,6 +175,7 @@ gimp_data_class_init (GimpDataClass *klass) g_object_class_install_property (object_class, PROP_IMAGE, g_param_spec_object ("image", NULL, NULL, GIMP_TYPE_IMAGE, + G_PARAM_EXPLICIT_NOTIFY | GIMP_PARAM_READWRITE)); g_object_class_install_property (object_class, PROP_WRITABLE, @@ -992,6 +993,8 @@ gimp_data_set_image (GimpData *data, private->writable = writable ? TRUE : FALSE; private->deletable = deletable ? TRUE : FALSE; + + g_object_notify (G_OBJECT (data), "image"); } GimpImage * diff --git a/app/core/gimpimage-colormap.c b/app/core/gimpimage-colormap.c index 57b4aa8f4a..d03aacbca9 100644 --- a/app/core/gimpimage-colormap.c +++ b/app/core/gimpimage-colormap.c @@ -88,7 +88,7 @@ gimp_image_colormap_init (GimpImage *image) palette_id = g_strdup_printf ("gimp-indexed-image-palette-%d", gimp_image_get_id (image)); - private->palette = GIMP_PALETTE (gimp_palette_new (NULL, palette_name)); + private->palette = GIMP_PALETTE (gimp_palette_new (NULL, palette_name)); gimp_image_colormap_update_formats (image); @@ -254,8 +254,6 @@ _gimp_image_get_colormap (GimpImage *image, gint *n_colors) { GimpImagePrivate *private; - const Babl *space; - const Babl *format; g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); @@ -264,10 +262,9 @@ _gimp_image_get_colormap (GimpImage *image, if (private->palette == NULL) return NULL; - space = gimp_image_get_layer_space (image); - format = gimp_babl_format (GIMP_RGB, private->precision, FALSE, space); + g_return_val_if_fail (private->palette->format != NULL, NULL); - return gimp_palette_get_colormap (private->palette, format, n_colors); + return gimp_palette_get_colormap (private->palette, private->palette->format, n_colors); } void @@ -277,12 +274,8 @@ _gimp_image_set_colormap (GimpImage *image, gboolean push_undo) { GimpImagePrivate *private; - GimpPaletteEntry *entry; const Babl *space; const Babl *format; - GeglColor *color; - gint bpp; - gint i; g_return_if_fail (GIMP_IS_IMAGE (image)); g_return_if_fail (colormap != NULL || n_colors == 0); @@ -290,31 +283,14 @@ _gimp_image_set_colormap (GimpImage *image, private = GIMP_IMAGE_GET_PRIVATE (image); - if (push_undo) - gimp_image_undo_push_image_colormap (image, C_("undo-type", "Set Colormap")); - - if (!private->palette) + if (private->palette == NULL) gimp_image_colormap_init (image); - gimp_data_freeze (GIMP_DATA (private->palette)); - - while ((entry = gimp_palette_get_entry (private->palette, 0))) - gimp_palette_delete_entry (private->palette, entry); - space = gimp_image_get_layer_space (image); format = gimp_babl_format (GIMP_RGB, private->precision, FALSE, space); - bpp = babl_format_get_bytes_per_pixel (format); - - color = gegl_color_new (NULL); - for (i = 0; i < n_colors; i++) - { - gegl_color_set_pixel (color, format, &colormap[i * bpp]); - gimp_image_colormap_set_palette_entry (image, color, i); - } - g_object_unref (color); + gimp_palette_set_colormap (private->palette, format, colormap, n_colors, push_undo); gimp_image_colormap_changed (image, -1); - gimp_data_thaw (GIMP_DATA (private->palette)); } void diff --git a/app/core/gimppalette.c b/app/core/gimppalette.c index 74535b34e7..33eda09994 100644 --- a/app/core/gimppalette.c +++ b/app/core/gimppalette.c @@ -28,6 +28,8 @@ #include "core-types.h" +#include "gegl/gimp-babl.h" + #include "gimp-memsize.h" #include "gimpimage.h" #include "gimpimage-undo-push.h" @@ -51,41 +53,47 @@ enum /* local function prototypes */ -static void gimp_palette_tagged_iface_init (GimpTaggedInterface *iface); +static void gimp_palette_tagged_iface_init (GimpTaggedInterface *iface); -static void gimp_palette_finalize (GObject *object); +static void gimp_palette_finalize (GObject *object); -static gint64 gimp_palette_get_memsize (GimpObject *object, - gint64 *gui_size); +static gint64 gimp_palette_get_memsize (GimpObject *object, + gint64 *gui_size); -static void gimp_palette_get_preview_size (GimpViewable *viewable, - gint size, - gboolean popup, - gboolean dot_for_dot, - gint *width, - gint *height); -static gboolean gimp_palette_get_popup_size (GimpViewable *viewable, - gint width, - gint height, - gboolean dot_for_dot, - gint *popup_width, - gint *popup_height); -static GimpTempBuf * gimp_palette_get_new_preview (GimpViewable *viewable, - GimpContext *context, - gint width, - gint height); -static gchar * gimp_palette_get_description (GimpViewable *viewable, - gchar **tooltip); -static const gchar * gimp_palette_get_extension (GimpData *data); -static void gimp_palette_copy (GimpData *data, - GimpData *src_data); -static void gimp_palette_real_entry_changed (GimpPalette *palette, - gint index); +static void gimp_palette_get_preview_size (GimpViewable *viewable, + gint size, + gboolean popup, + gboolean dot_for_dot, + gint *width, + gint *height); +static gboolean gimp_palette_get_popup_size (GimpViewable *viewable, + gint width, + gint height, + gboolean dot_for_dot, + gint *popup_width, + gint *popup_height); +static GimpTempBuf * gimp_palette_get_new_preview (GimpViewable *viewable, + GimpContext *context, + gint width, + gint height); +static gchar * gimp_palette_get_description (GimpViewable *viewable, + gchar **tooltip); +static const gchar * gimp_palette_get_extension (GimpData *data); +static void gimp_palette_copy (GimpData *data, + GimpData *src_data); +static void gimp_palette_real_entry_changed (GimpPalette *palette, + gint index); -static void gimp_palette_entry_free (GimpPaletteEntry *entry); -static gint64 gimp_palette_entry_get_memsize (GimpPaletteEntry *entry, - gint64 *gui_size); -static gchar * gimp_palette_get_checksum (GimpTagged *tagged); +static void gimp_palette_entry_free (GimpPaletteEntry *entry); +static gint64 gimp_palette_entry_get_memsize (GimpPaletteEntry *entry, + gint64 *gui_size); +static gchar * gimp_palette_get_checksum (GimpTagged *tagged); + +static void gimp_palette_image_space_updated (GimpImage *image, + GimpPalette *palette); +static void gimp_palette_notify_image (GimpPalette *palette, + const GParamSpec *unused, + gpointer unused_user_data); G_DEFINE_TYPE_WITH_CODE (GimpPalette, gimp_palette, GIMP_TYPE_DATA, @@ -141,6 +149,12 @@ gimp_palette_init (GimpPalette *palette) palette->colors = NULL; palette->n_colors = 0; palette->n_columns = 0; + palette->image = NULL; + palette->format = NULL; + + g_signal_connect (palette, "notify::image", + G_CALLBACK (gimp_palette_notify_image), + NULL); } static void @@ -155,6 +169,8 @@ gimp_palette_finalize (GObject *object) palette->colors = NULL; } + g_clear_weak_pointer (&palette->image); + G_OBJECT_CLASS (parent_class)->finalize (object); } @@ -401,6 +417,58 @@ gimp_palette_get_checksum (GimpTagged *tagged) return checksum_string; } +static void +gimp_palette_image_space_updated (GimpImage *image, + GimpPalette *palette) +{ + const Babl *space; + const Babl *format; + + space = gimp_image_get_layer_space (image); + format = gimp_babl_format (GIMP_RGB, gimp_image_get_precision (image), FALSE, space); + gimp_palette_restrict_format (palette, format); +} + +static void +gimp_palette_notify_image (GimpPalette *palette, + const GParamSpec *unused, + gpointer unused_user_data) +{ + GimpImage *image = gimp_data_get_image (GIMP_DATA (palette)); + + if (palette->image == image) + return; + + if (palette->image) + g_signal_handlers_disconnect_by_func (palette->image, + G_CALLBACK (gimp_palette_image_space_updated), + palette); + + g_set_weak_pointer (&palette->image, image); + + if (image) + { + /* Note: I only connect to "precision-changed", and not + * "profile-changed" changes which is handled in + * gimp_image_convert_profile_colormap() with additional bpc + * argument. + * In current implementation of indexed images, + * "precision-changed" should not happen because it is always + * 8-bit non-linear. + */ + g_signal_connect_object (image, + "precision-changed", + G_CALLBACK (gimp_palette_image_space_updated), + palette, 0); + + gimp_palette_image_space_updated (image, palette); + } + else + { + gimp_palette_restrict_format (palette, NULL); + } +} + /* public functions */ @@ -799,6 +867,53 @@ gimp_palette_get_colormap (GimpPalette *palette, return colormap; } +void +gimp_palette_set_colormap (GimpPalette *palette, + const Babl *format, + const guchar *colormap, + gint n_colors, + gboolean push_undo_if_image) +{ + GimpPaletteEntry *entry; + GeglColor *color; + gchar name[64]; + gint bpp; + gint i; + + g_return_if_fail (GIMP_IS_PALETTE (palette)); + g_return_if_fail (format != NULL); + g_return_if_fail (n_colors > 0); + + if (push_undo_if_image && gimp_data_get_image (GIMP_DATA (palette))) + gimp_image_undo_push_image_colormap (gimp_data_get_image (GIMP_DATA (palette)), + C_("undo-type", "Set Colormap")); + + if (gimp_data_get_image (GIMP_DATA (palette))) + n_colors = MIN (n_colors, 256); + + gimp_data_freeze (GIMP_DATA (palette)); + + while ((entry = gimp_palette_get_entry (palette, 0))) + gimp_palette_delete_entry (palette, entry); + + bpp = babl_format_get_bytes_per_pixel (format); + + color = gegl_color_new (NULL); + for (i = 0; i < n_colors; i++) + { + gegl_color_set_pixel (color, format, &colormap[i * bpp]); + g_snprintf (name, sizeof (name), "#%d", i); + gimp_palette_add_entry (palette, i, name, color); + } + g_object_unref (color); + + gimp_data_thaw (GIMP_DATA (palette)); + + if (! gimp_data_is_frozen (GIMP_DATA (palette))) + for (gint i = 0; i < n_colors; i++) + g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i); +} + /* private functions */ diff --git a/app/core/gimppalette.h b/app/core/gimppalette.h index 06dc7ee8c5..b8e2efac8d 100644 --- a/app/core/gimppalette.h +++ b/app/core/gimppalette.h @@ -43,6 +43,13 @@ struct _GimpPalette { GimpData parent_instance; + /* When set to an image, we store the image we connect to, so that we + * can correctly disconnect if parent GimpData's image changes (so the + * GimpPalette and GimpData's images may be unsynced for a tiny span of + * time). + */ + GimpImage *image; + /* Palette colors can be restricted to a given format. If NULL, then the * palette can be a mix of color models and color spaces. */ @@ -114,6 +121,11 @@ GimpPaletteEntry * gimp_palette_find_entry (GimpPalette *palette, guchar * gimp_palette_get_colormap (GimpPalette *palette, const Babl *format, gint *n_colors); +void gimp_palette_set_colormap (GimpPalette *palette, + const Babl *format, + const guchar *colormap, + gint n_colors, + gboolean push_undo_if_image); #endif /* __GIMP_PALETTE_H__ */ diff --git a/app/pdb/internal-procs.c b/app/pdb/internal-procs.c index bdb3648066..c32b4522bb 100644 --- a/app/pdb/internal-procs.c +++ b/app/pdb/internal-procs.c @@ -30,7 +30,7 @@ #include "internal-procs.h" -/* 779 procedures registered total */ +/* 780 procedures registered total */ void internal_procs_init (GimpPDB *pdb) diff --git a/app/pdb/palette-cmds.c b/app/pdb/palette-cmds.c index 3f93732602..b80632c630 100644 --- a/app/pdb/palette-cmds.c +++ b/app/pdb/palette-cmds.c @@ -492,6 +492,49 @@ palette_get_colormap_invoker (GimpProcedure *procedure, return return_vals; } +static GimpValueArray * +palette_set_colormap_invoker (GimpProcedure *procedure, + Gimp *gimp, + GimpContext *context, + GimpProgress *progress, + const GimpValueArray *args, + GError **error) +{ + gboolean success = TRUE; + GimpPalette *palette; + const Babl *format; + GBytes *colormap; + + palette = g_value_get_object (gimp_value_array_index (args, 0)); + format = g_value_get_boxed (gimp_value_array_index (args, 1)); + colormap = g_value_get_boxed (gimp_value_array_index (args, 2)); + + if (success) + { + gint bpp; + + bpp = babl_format_get_bytes_per_pixel (format); + + if (g_bytes_get_size (colormap) % bpp == 0) + { + const guchar *data; + gsize n_bytes; + gint n_colors; + + data = g_bytes_get_data (colormap, &n_bytes), + n_colors = n_bytes / bpp; + gimp_palette_set_colormap (palette, format, (guchar *) data, n_colors, TRUE); + } + else + { + success = FALSE; + } + } + + return gimp_procedure_get_return_values (procedure, success, + error ? *error : NULL); +} + void register_palette_procs (GimpPDB *pdb) { @@ -961,4 +1004,42 @@ register_palette_procs (GimpPDB *pdb) GIMP_PARAM_READWRITE)); gimp_pdb_register_procedure (pdb, procedure); g_object_unref (procedure); + + /* + * gimp-palette-set-colormap + */ + procedure = gimp_procedure_new (palette_set_colormap_invoker); + gimp_object_set_static_name (GIMP_OBJECT (procedure), + "gimp-palette-set-colormap"); + gimp_procedure_set_static_help (procedure, + "Sets the entries in the image's colormap.", + "This procedure sets the entries in the specified palette in one go. The number of entries depens on the size of @colormap and the bytes-per-pixel size of @format.\n" + "The procedure will fail if the size of @colormap is not an exact multiple of the number of bytes per pixel of @format.", + NULL); + gimp_procedure_set_static_attribution (procedure, + "Jehan", + "Jehan", + "2024"); + gimp_procedure_add_argument (procedure, + gimp_param_spec_palette ("palette", + "palette", + "The palette", + FALSE, + NULL, + FALSE, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + g_param_spec_boxed ("format", + "format", + "The desired color format", + GIMP_TYPE_BABL_FORMAT, + GIMP_PARAM_READWRITE)); + gimp_procedure_add_argument (procedure, + g_param_spec_boxed ("colormap", + "colormap", + "The new colormap values", + G_TYPE_BYTES, + GIMP_PARAM_READWRITE)); + gimp_pdb_register_procedure (pdb, procedure); + g_object_unref (procedure); } diff --git a/libgimp/gimp.def b/libgimp/gimp.def index a932caba9e..e2d1624cc7 100644 --- a/libgimp/gimp.def +++ b/libgimp/gimp.def @@ -674,6 +674,7 @@ EXPORTS gimp_palette_get_columns gimp_palette_get_type gimp_palette_new + gimp_palette_set_colormap gimp_palette_set_columns gimp_palettes_close_popup gimp_palettes_get_list diff --git a/libgimp/gimppalette_pdb.c b/libgimp/gimppalette_pdb.c index a3d980e131..34a7b6bb2f 100644 --- a/libgimp/gimppalette_pdb.c +++ b/libgimp/gimppalette_pdb.c @@ -578,3 +578,48 @@ gimp_palette_get_colormap (GimpPalette *palette, return colormap; } + +/** + * gimp_palette_set_colormap: + * @palette: The palette. + * @format: The desired color format. + * @colormap: The new colormap values. + * + * Sets the entries in the image's colormap. + * + * This procedure sets the entries in the specified palette in one go. + * The number of entries depens on the size of @colormap and the + * bytes-per-pixel size of @format. + * The procedure will fail if the size of @colormap is not an exact + * multiple of the number of bytes per pixel of @format. + * + * Returns: TRUE on success. + * + * Since: 3.0 + **/ +gboolean +gimp_palette_set_colormap (GimpPalette *palette, + const Babl *format, + GBytes *colormap) +{ + GimpValueArray *args; + GimpValueArray *return_vals; + gboolean success = TRUE; + + args = gimp_value_array_new_from_types (NULL, + GIMP_TYPE_PALETTE, palette, + GIMP_TYPE_BABL_FORMAT, format, + G_TYPE_BYTES, colormap, + G_TYPE_NONE); + + return_vals = _gimp_pdb_run_procedure_array (gimp_get_pdb (), + "gimp-palette-set-colormap", + args); + gimp_value_array_unref (args); + + success = GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS; + + gimp_value_array_unref (return_vals); + + return success; +} diff --git a/libgimp/gimppalette_pdb.h b/libgimp/gimppalette_pdb.h index f6dc3a8e6d..025af10b39 100644 --- a/libgimp/gimppalette_pdb.h +++ b/libgimp/gimppalette_pdb.h @@ -59,6 +59,9 @@ gboolean gimp_palette_entry_set_name (GimpPalette *palette, GBytes* gimp_palette_get_colormap (GimpPalette *palette, const Babl *format, gint *num_colors); +gboolean gimp_palette_set_colormap (GimpPalette *palette, + const Babl *format, + GBytes *colormap); G_END_DECLS diff --git a/pdb/groups/palette.pdb b/pdb/groups/palette.pdb index 99754cb1f9..a3b3908992 100644 --- a/pdb/groups/palette.pdb +++ b/pdb/groups/palette.pdb @@ -475,6 +475,55 @@ CODE ); } +sub palette_set_colormap { + $blurb = "Sets the entries in the image's colormap."; + + $help = <<'HELP'; +This procedure sets the entries in the specified palette in one go. The number +of entries depens on the size of @colormap and the bytes-per-pixel size of @format. + +The procedure will fail if the size of @colormap is not an exact +multiple of the number of bytes per pixel of @format. +HELP + + &jehan_pdb_misc('2024', '3.0'); + + @inargs = ( + { name => 'palette', type => 'palette', + desc => 'The palette' }, + { name => 'format', type => 'format', + desc => 'The desired color format' }, + { name => 'colormap', type => 'bytes', + desc => 'The new colormap values' } + ); + + %invoke = ( + headers => [ qw("core/gimpimage-colormap.h") ], + code => <<'CODE' +{ + gint bpp; + + bpp = babl_format_get_bytes_per_pixel (format); + + if (g_bytes_get_size (colormap) % bpp == 0) + { + const guchar *data; + gsize n_bytes; + gint n_colors; + + data = g_bytes_get_data (colormap, &n_bytes), + n_colors = n_bytes / bpp; + gimp_palette_set_colormap (palette, format, (guchar *) data, n_colors, TRUE); + } + else + { + success = FALSE; + } +} +CODE + ); +} + @headers = qw( "core/gimp.h" "core/gimpcontext.h" @@ -491,7 +540,7 @@ CODE palette_add_entry palette_delete_entry palette_entry_get_color palette_entry_set_color palette_entry_get_name palette_entry_set_name - palette_get_colormap); + palette_get_colormap palette_set_colormap); %exports = (app => [@procs], lib => [@procs]);