app, libgimp, pdb: new gimp_image_get_palette().

This is meant to replace gimp_image_get_colormap() (see also #9477).

We likely won't need a gimp_image_set_palette() because we can simply edit the
image's colormap/palette with GimpPalette API now and it is directly updated.

For instance, the following code changes the first entry in the image palette to
red, immediately:

```python
i = Gimp.list_images()[0]
p = i.get_palette()
c = Gimp.RGB()
c.r = 1.0
p.entry_set_color(0, c)
```

For this to work fine, I added a new concept to GimpData, which is that they can
be tied to a GimpImage (instead of a GFile). Image palettes are not considered
internals, they are just tied to their image, therefore they can be edited by
scripts/plug-ins.

Additionally with this commit, editing an image's colormap from libgimp API also
generates undo steps now.
This commit is contained in:
Jehan 2023-10-06 16:16:57 +02:00
parent c54a33f0ff
commit d931098d36
17 changed files with 290 additions and 17 deletions

View File

@ -30,6 +30,7 @@
#include "gimp-memsize.h"
#include "gimpdata.h"
#include "gimpidtable.h"
#include "gimpimage.h"
#include "gimptag.h"
#include "gimptagged.h"
@ -47,6 +48,7 @@ enum
PROP_0,
PROP_ID,
PROP_FILE,
PROP_IMAGE,
PROP_WRITABLE,
PROP_DELETABLE,
PROP_MIME_TYPE
@ -55,8 +57,10 @@ enum
struct _GimpDataPrivate
{
gint ID;
GFile *file;
gint ID;
GFile *file;
GimpImage *image;
GQuark mime_type;
guint writable : 1;
guint deletable : 1;
@ -168,6 +172,11 @@ gimp_data_class_init (GimpDataClass *klass)
G_TYPE_FILE,
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_IMAGE,
g_param_spec_object ("image", NULL, NULL,
GIMP_TYPE_IMAGE,
GIMP_PARAM_READWRITE));
g_object_class_install_property (object_class, PROP_WRITABLE,
g_param_spec_boolean ("writable", NULL, NULL,
FALSE,
@ -264,6 +273,12 @@ gimp_data_set_property (GObject *object,
private->deletable);
break;
case PROP_IMAGE:
gimp_data_set_image (data,
g_value_get_object (value),
private->writable,
private->deletable);
break;
case PROP_WRITABLE:
private->writable = g_value_get_boolean (value);
break;
@ -303,6 +318,10 @@ gimp_data_get_property (GObject *object,
g_value_set_object (value, private->file);
break;
case PROP_IMAGE:
g_value_set_object (value, private->image);
break;
case PROP_WRITABLE:
g_value_set_boolean (value, private->writable);
break;
@ -458,7 +477,7 @@ gimp_data_get_identifier (GimpTagged *tagged)
gchar *identifier = NULL;
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
collection = gimp_data_get_collection (data);
/* The identifier is guaranteed to be unique because we use 2 directory
@ -499,7 +518,7 @@ gimp_data_get_collection (GimpData *data)
GimpDataPrivate *private = GIMP_DATA_GET_PRIVATE (data);
gchar *collection = NULL;
g_return_val_if_fail (private->internal || private->file != NULL, NULL);
g_return_val_if_fail (private->internal || private->file != NULL || private->image != NULL, NULL);
if (private->file)
{
@ -547,6 +566,10 @@ gimp_data_get_collection (GimpData *data)
g_free (path);
}
else if (private->image)
{
collection = g_strdup_printf ("[image-id-%d]", gimp_image_get_id (private->image));
}
else if (private->internal)
{
collection = g_strdup (private->collection);
@ -599,7 +622,7 @@ gimp_data_save (GimpData *data,
g_return_val_if_fail (private->writable == TRUE, FALSE);
if (private->internal)
if (private->internal || private->image != NULL)
{
private->dirty = FALSE;
return TRUE;
@ -858,6 +881,8 @@ gimp_data_set_file (GimpData *data,
if (private->internal)
return;
g_return_if_fail (private->image == NULL);
g_set_object (&private->file, file);
private->writable = FALSE;
@ -933,6 +958,53 @@ gimp_data_get_file (GimpData *data)
return private->file;
}
/**
* gimp_data_set_image:
* @data: A #GimpData object
* @image: Image to assign to @data.
* @writable: %TRUE if we want to be able to write to this file.
* @deletable: %TRUE if we want to be able to delete this file.
*
* This function assigns an image to @data. This can only be done if no file has
* been assigned (a non-internal data can be attached either to a file or to an
* image).
**/
void
gimp_data_set_image (GimpData *data,
GimpImage *image,
gboolean writable,
gboolean deletable)
{
GimpDataPrivate *private;
g_return_if_fail (GIMP_IS_DATA (data));
g_return_if_fail (GIMP_IS_IMAGE (image));
private = GIMP_DATA_GET_PRIVATE (data);
if (private->internal)
return;
g_return_if_fail (private->file == NULL);
g_set_object (&private->image, image);
private->writable = writable ? TRUE : FALSE;
private->deletable = deletable ? TRUE : FALSE;
}
GimpImage *
gimp_data_get_image (GimpData *data)
{
GimpDataPrivate *private;
g_return_val_if_fail (GIMP_IS_DATA (data), NULL);
private = GIMP_DATA_GET_PRIVATE (data);
return private->image;
}
/**
* gimp_data_create_filename:
* @data: a #Gimpdata object.

View File

@ -98,6 +98,11 @@ void gimp_data_set_file (GimpData *data,
gboolean writable,
gboolean deletable);
GFile * gimp_data_get_file (GimpData *data);
void gimp_data_set_image (GimpData *data,
GimpImage *image,
gboolean writable,
gboolean deletable);
GimpImage * gimp_data_get_image (GimpData *data);
void gimp_data_create_filename (GimpData *data,
GFile *dest_dir);

View File

@ -399,6 +399,9 @@ gimp_data_factory_real_data_save (GimpDataFactory *factory)
GimpData *data = list->data;
GError *error = NULL;
if (gimp_data_get_image (data))
continue;
if (! gimp_data_get_file (data))
gimp_data_create_filename (data, writable_dir);
@ -752,7 +755,7 @@ gimp_data_factory_data_save_single (GimpDataFactory *factory,
g_return_val_if_fail (GIMP_IS_DATA (data), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (! gimp_data_is_dirty (data))
if (! gimp_data_is_dirty (data) || gimp_data_get_image (data))
return TRUE;
if (! gimp_data_get_file (data))

View File

@ -76,7 +76,7 @@ gimp_image_colormap_init (GimpImage *image)
gimp_palette_set_columns (private->palette, 16);
gimp_data_make_internal (GIMP_DATA (private->palette), palette_id);
gimp_data_set_image (GIMP_DATA (private->palette), image, TRUE, FALSE);
palettes = gimp_data_factory_get_container (image->gimp->palette_factory);
@ -315,9 +315,8 @@ gimp_image_set_colormap (GimpImage *image,
gimp_image_colormap_set_palette_entry (image, &color, i);
}
gimp_data_thaw (GIMP_DATA (private->palette));
gimp_image_colormap_changed (image, -1);
gimp_data_thaw (GIMP_DATA (private->palette));
}
void

View File

@ -29,6 +29,8 @@
#include "core-types.h"
#include "gimp-memsize.h"
#include "gimpimage.h"
#include "gimpimage-undo-push.h"
#include "gimppalette.h"
#include "gimppalette-load.h"
#include "gimppalette-save.h"
@ -40,6 +42,12 @@
#define RGB_EPSILON 1e-6
enum
{
ENTRY_CHANGED,
LAST_SIGNAL
};
/* local function prototypes */
@ -71,6 +79,8 @@ static gchar * gimp_palette_get_description (GimpViewable *viewa
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,
@ -84,6 +94,7 @@ G_DEFINE_TYPE_WITH_CODE (GimpPalette, gimp_palette, GIMP_TYPE_DATA,
#define parent_class gimp_palette_parent_class
static guint signals[LAST_SIGNAL] = { 0 };
static void
gimp_palette_class_init (GimpPaletteClass *klass)
@ -93,6 +104,14 @@ gimp_palette_class_init (GimpPaletteClass *klass)
GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
GimpDataClass *data_class = GIMP_DATA_CLASS (klass);
signals[ENTRY_CHANGED] = g_signal_new ("entry-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (GimpPaletteClass, entry_changed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
G_TYPE_INT);
object_class->finalize = gimp_palette_finalize;
gimp_object_class->get_memsize = gimp_palette_get_memsize;
@ -106,6 +125,8 @@ gimp_palette_class_init (GimpPaletteClass *klass)
data_class->save = gimp_palette_save;
data_class->get_extension = gimp_palette_get_extension;
data_class->copy = gimp_palette_copy;
klass->entry_changed = gimp_palette_real_entry_changed;
}
static void
@ -340,6 +361,16 @@ gimp_palette_copy (GimpData *data,
gimp_data_thaw (data);
}
static void
gimp_palette_real_entry_changed (GimpPalette *palette,
gint index)
{
GimpImage *image = gimp_data_get_image (GIMP_DATA (palette));
if (image != NULL)
gimp_image_colormap_changed (image, index);
}
static gchar *
gimp_palette_get_checksum (GimpTagged *tagged)
{
@ -402,11 +433,17 @@ gimp_palette_move_entry (GimpPalette *palette,
if (g_list_find (palette->colors, entry))
{
gint old_position = g_list_index (palette->colors, entry);
palette->colors = g_list_remove (palette->colors,
entry);
palette->colors = g_list_insert (palette->colors,
entry, position);
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
for (gint i = MIN (position, old_position); i <= MAX (position, old_position); i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
gimp_data_dirty (GIMP_DATA (palette));
}
}
@ -438,6 +475,10 @@ gimp_palette_add_entry (GimpPalette *palette,
palette->n_colors += 1;
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
for (gint i = position; i < palette->n_colors; i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
gimp_data_dirty (GIMP_DATA (palette));
return entry;
@ -452,12 +493,18 @@ gimp_palette_delete_entry (GimpPalette *palette,
if (g_list_find (palette->colors, entry))
{
gint old_position = g_list_index (palette->colors, entry);
gimp_palette_entry_free (entry);
palette->colors = g_list_remove (palette->colors, entry);
palette->n_colors--;
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
for (gint i = old_position; i < palette->n_colors; i++)
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, i);
gimp_data_dirty (GIMP_DATA (palette));
}
}
@ -485,6 +532,9 @@ gimp_palette_set_entry (GimpPalette *palette,
entry->name = g_strdup (name);
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
gimp_data_dirty (GIMP_DATA (palette));
return TRUE;
@ -493,7 +543,8 @@ gimp_palette_set_entry (GimpPalette *palette,
gboolean
gimp_palette_set_entry_color (GimpPalette *palette,
gint position,
const GimpRGB *color)
const GimpRGB *color,
gboolean push_undo_if_image)
{
GimpPaletteEntry *entry;
@ -505,8 +556,15 @@ gimp_palette_set_entry_color (GimpPalette *palette,
if (! entry)
return FALSE;
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", "Change Colormap entry"));
entry->color = *color;
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
gimp_data_dirty (GIMP_DATA (palette));
return TRUE;
@ -531,6 +589,9 @@ gimp_palette_set_entry_name (GimpPalette *palette,
entry->name = g_strdup (name);
if (! gimp_data_is_frozen (GIMP_DATA (palette)))
g_signal_emit (palette, signals[ENTRY_CHANGED], 0, position);
gimp_data_dirty (GIMP_DATA (palette));
return TRUE;

View File

@ -52,6 +52,9 @@ struct _GimpPalette
struct _GimpPaletteClass
{
GimpDataClass parent_class;
void (* entry_changed) (GimpPalette *palette,
gint index);
};
@ -81,7 +84,8 @@ gboolean gimp_palette_set_entry (GimpPalette *palette,
const GimpRGB *color);
gboolean gimp_palette_set_entry_color (GimpPalette *palette,
gint position,
const GimpRGB *color);
const GimpRGB *color,
gboolean push_undo_if_image);
gboolean gimp_palette_set_entry_name (GimpPalette *palette,
gint position,
const gchar *name);

View File

@ -213,7 +213,7 @@ gimp_palette_mru_add (GimpPaletteMru *mru,
/* Even though they are nearly the same color, let's make them
* exactly equal.
*/
gimp_palette_set_entry_color (palette, 0, color);
gimp_palette_set_entry_color (palette, 0, color, FALSE);
return;
}

View File

@ -51,6 +51,7 @@
#include "core/gimpimage.h"
#include "core/gimpitem.h"
#include "core/gimplayer.h"
#include "core/gimppalette.h"
#include "core/gimpparamspecs.h"
#include "core/gimppickable.h"
#include "core/gimpprogress.h"
@ -1571,6 +1572,35 @@ image_set_colormap_invoker (GimpProcedure *procedure,
error ? *error : NULL);
}
static GimpValueArray *
image_get_palette_invoker (GimpProcedure *procedure,
Gimp *gimp,
GimpContext *context,
GimpProgress *progress,
const GimpValueArray *args,
GError **error)
{
gboolean success = TRUE;
GimpValueArray *return_vals;
GimpImage *image;
GimpPalette *colormap = NULL;
image = g_value_get_object (gimp_value_array_index (args, 0));
if (success)
{
colormap = gimp_image_get_colormap_palette (image);
}
return_vals = gimp_procedure_get_return_values (procedure, success,
error ? *error : NULL);
if (success)
g_value_set_object (gimp_value_array_index (return_vals, 1), colormap);
return return_vals;
}
static GimpValueArray *
image_get_metadata_invoker (GimpProcedure *procedure,
Gimp *gimp,
@ -4352,6 +4382,35 @@ register_image_procs (GimpPDB *pdb)
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-image-get-palette
*/
procedure = gimp_procedure_new (image_get_palette_invoker);
gimp_object_set_static_name (GIMP_OBJECT (procedure),
"gimp-image-get-palette");
gimp_procedure_set_static_help (procedure,
"Returns the image's colormap",
"This procedure returns the image's colormap as a GimpPalette. If the image is not in Indexed color mode, %NULL is returned.",
NULL);
gimp_procedure_set_static_attribution (procedure,
"Jehan",
"Jehan",
"2023");
gimp_procedure_add_argument (procedure,
gimp_param_spec_image ("image",
"image",
"The image",
FALSE,
GIMP_PARAM_READWRITE));
gimp_procedure_add_return_value (procedure,
gimp_param_spec_palette ("colormap",
"colormap",
"The image's colormap.",
FALSE,
GIMP_PARAM_READWRITE));
gimp_pdb_register_procedure (pdb, procedure);
g_object_unref (procedure);
/*
* gimp-image-get-metadata
*/

View File

@ -30,7 +30,7 @@
#include "internal-procs.h"
/* 779 procedures registered total */
/* 780 procedures registered total */
void
internal_procs_init (GimpPDB *pdb)

View File

@ -372,7 +372,7 @@ palette_entry_set_color_invoker (GimpProcedure *procedure,
if (success)
{
if (gimp_data_is_writable (GIMP_DATA (palette)))
success = gimp_palette_set_entry_color (palette, entry_num, &color);
success = gimp_palette_set_entry_color (palette, entry_num, &color, TRUE);
else
success = FALSE;
}

View File

@ -534,7 +534,7 @@ gimp_color_history_color_changed (GtkWidget *widget,
gimp_color_area_get_color (GIMP_COLOR_AREA (widget), &color);
gimp_palette_set_entry_color (palette, GPOINTER_TO_INT (data), &color);
gimp_palette_set_entry_color (palette, GPOINTER_TO_INT (data), &color, FALSE);
}
static void

View File

@ -556,7 +556,7 @@ gimp_palette_editor_pick_color (GimpPaletteEditor *editor,
case GIMP_COLOR_PICK_STATE_UPDATE:
case GIMP_COLOR_PICK_STATE_END:
gimp_palette_set_entry_color (GIMP_PALETTE (data),
index, color);
index, color, FALSE);
break;
}
}

View File

@ -407,6 +407,7 @@ EXPORTS
gimp_image_get_layers
gimp_image_get_metadata
gimp_image_get_name
gimp_image_get_palette
gimp_image_get_parasite
gimp_image_get_parasite_list
gimp_image_get_precision

View File

@ -1821,6 +1821,43 @@ _gimp_image_set_colormap (GimpImage *image,
return success;
}
/**
* gimp_image_get_palette:
* @image: The image.
*
* Returns the image's colormap
*
* This procedure returns the image's colormap as a GimpPalette. If the
* image is not in Indexed color mode, %NULL is returned.
*
* Returns: (transfer none): The image's colormap.
*
* Since: 3.0
**/
GimpPalette *
gimp_image_get_palette (GimpImage *image)
{
GimpValueArray *args;
GimpValueArray *return_vals;
GimpPalette *colormap = NULL;
args = gimp_value_array_new_from_types (NULL,
GIMP_TYPE_IMAGE, image,
G_TYPE_NONE);
return_vals = gimp_pdb_run_procedure_array (gimp_get_pdb (),
"gimp-image-get-palette",
args);
gimp_value_array_unref (args);
if (GIMP_VALUES_GET_ENUM (return_vals, 0) == GIMP_PDB_SUCCESS)
colormap = GIMP_VALUES_GET_PALETTE (return_vals, 1);
gimp_value_array_unref (return_vals);
return colormap;
}
/**
* _gimp_image_get_metadata:
* @image: The image.

View File

@ -118,6 +118,7 @@ GimpLayer* gimp_image_merge_layer_group (GimpImage
G_GNUC_INTERNAL GBytes* _gimp_image_get_colormap (GimpImage *image);
G_GNUC_INTERNAL gboolean _gimp_image_set_colormap (GimpImage *image,
GBytes *colormap);
GimpPalette* gimp_image_get_palette (GimpImage *image);
G_GNUC_INTERNAL gchar* _gimp_image_get_metadata (GimpImage *image);
G_GNUC_INTERNAL gboolean _gimp_image_set_metadata (GimpImage *image,
const gchar *metadata_string);

View File

@ -1563,6 +1563,36 @@ CODE
);
}
sub image_get_palette {
$blurb = "Returns the image's colormap";
$help = <<'HELP';
This procedure returns the image's colormap as a GimpPalette.
If the image is not in Indexed color mode, %NULL is returned.
HELP
&jehan_pdb_misc('2023', '3.0');
@inargs = (
{ name => 'image', type => 'image',
desc => 'The image' }
);
@outargs = (
{ name => 'colormap', type => 'palette',
desc => "The image's colormap." }
);
%invoke = (
headers => [ qw("core/gimpimage-colormap.h") ],
code => <<'CODE'
{
colormap = gimp_image_get_colormap_palette (image);
}
CODE
);
}
sub image_get_metadata {
$blurb = "Returns the image's metadata.";
$help = 'Returns exif/iptc/xmp metadata from the image.';
@ -3207,6 +3237,7 @@ CODE
image_flatten image_merge_visible_layers image_merge_down
image_merge_layer_group
image_get_colormap image_set_colormap
image_get_palette
image_get_metadata image_set_metadata
image_clean_all image_is_dirty
image_thumbnail

View File

@ -341,7 +341,7 @@ HELP
code => <<'CODE'
{
if (gimp_data_is_writable (GIMP_DATA (palette)))
success = gimp_palette_set_entry_color (palette, entry_num, &color);
success = gimp_palette_set_entry_color (palette, entry_num, &color, TRUE);
else
success = FALSE;
}