app, pdb: make it possible to delete a color from a colormap if unused.
Until now, it was not really possible to delete a colormap color, but since we now use GimpPalette, people would definitely try to do so. It just makes sense to allow doing this, but only if the color is unused. Additionally when we do this, all the pixels refering to bigger indexes will be edited so that they continue to refer to the same color (bigger indexes are shifted by -1). Therefore removing an unused color does not change the image render. I wondered if we might want more options, e.g. the ability to delete a color without fixing indexes (i.e. that colors over the deleted color index would shift to the next color). This would even allow to delete used colors (though now the last index would have to be unused one, unless we cycle colors). Yet I don't think this should belong to this basic API. The most expected behavior when deleting a color from an image colormap is to fix all indexes stored in pixels so that the image still shows the same. So that's what this function will do in this generic usage.
This commit is contained in:
parent
c3c0a70dd5
commit
dbaa8b6a1c
10 changed files with 248 additions and 10 deletions
|
|
@ -1195,6 +1195,7 @@ gimp_undo_type_get_type (void)
|
|||
{ GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, "GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE", "group-image-vectors-merge" },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, "GIMP_UNDO_GROUP_IMAGE_QUICK_MASK", "group-image-quick-mask" },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_GRID, "GIMP_UNDO_GROUP_IMAGE_GRID", "group-image-grid" },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP, "GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP", "group-image-colormap-remap" },
|
||||
{ GIMP_UNDO_GROUP_GUIDE, "GIMP_UNDO_GROUP_GUIDE", "group-guide" },
|
||||
{ GIMP_UNDO_GROUP_SAMPLE_POINT, "GIMP_UNDO_GROUP_SAMPLE_POINT", "group-sample-point" },
|
||||
{ GIMP_UNDO_GROUP_DRAWABLE, "GIMP_UNDO_GROUP_DRAWABLE", "group-drawable" },
|
||||
|
|
@ -1303,6 +1304,7 @@ gimp_undo_type_get_type (void)
|
|||
{ GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, NC_("undo-type", "Merge paths"), NULL },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, NC_("undo-type", "Quick Mask"), NULL },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_GRID, NC_("undo-type", "Grid"), NULL },
|
||||
{ GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP, NC_("undo-type", "Colormap remapping"), NULL },
|
||||
{ GIMP_UNDO_GROUP_GUIDE, NC_("undo-type", "Guide"), NULL },
|
||||
{ GIMP_UNDO_GROUP_SAMPLE_POINT, NC_("undo-type", "Sample Point"), NULL },
|
||||
{ GIMP_UNDO_GROUP_DRAWABLE, NC_("undo-type", "Layer/Channel"), NULL },
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ typedef enum /*< pdb-skip >*/
|
|||
GIMP_UNDO_GROUP_IMAGE_VECTORS_MERGE, /*< desc="Merge paths" >*/
|
||||
GIMP_UNDO_GROUP_IMAGE_QUICK_MASK, /*< desc="Quick Mask" >*/
|
||||
GIMP_UNDO_GROUP_IMAGE_GRID, /*< desc="Grid" >*/
|
||||
GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP, /*< desc="Colormap remapping" >*/
|
||||
GIMP_UNDO_GROUP_GUIDE, /*< desc="Guide" >*/
|
||||
GIMP_UNDO_GROUP_SAMPLE_POINT, /*< desc="Sample Point" >*/
|
||||
GIMP_UNDO_GROUP_DRAWABLE, /*< desc="Layer/Channel" >*/
|
||||
|
|
|
|||
|
|
@ -28,13 +28,16 @@
|
|||
#include "core-types.h"
|
||||
|
||||
#include "gegl/gimp-babl.h"
|
||||
#include "gegl/gimp-gegl-loops.h"
|
||||
|
||||
#include "gimp.h"
|
||||
#include "gimpcontainer.h"
|
||||
#include "gimpdatafactory.h"
|
||||
#include "gimpdrawable.h"
|
||||
#include "gimpimage.h"
|
||||
#include "gimpimage-colormap.h"
|
||||
#include "gimpimage-private.h"
|
||||
#include "gimpimage-undo.h"
|
||||
#include "gimpimage-undo-push.h"
|
||||
#include "gimppalette.h"
|
||||
|
||||
|
|
@ -342,6 +345,30 @@ gimp_image_unset_colormap (GimpImage *image,
|
|||
gimp_image_colormap_changed (image, -1);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_image_colormap_is_index_used (GimpImage *image,
|
||||
gint color_index)
|
||||
{
|
||||
GList *layers;
|
||||
GList *iter;
|
||||
gboolean found;
|
||||
|
||||
layers = gimp_image_get_layer_list (image);
|
||||
|
||||
for (iter = layers; iter; iter = g_list_next (iter))
|
||||
{
|
||||
found = gimp_gegl_is_index_used (gimp_drawable_get_buffer (iter->data), NULL,
|
||||
gimp_drawable_get_format_without_alpha (iter->data),
|
||||
color_index);
|
||||
if (found)
|
||||
break;
|
||||
}
|
||||
|
||||
g_list_free (layers);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_image_get_colormap_entry (GimpImage *image,
|
||||
gint color_index,
|
||||
|
|
@ -415,6 +442,57 @@ gimp_image_add_colormap_entry (GimpImage *image,
|
|||
gimp_image_colormap_changed (image, -1);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_image_delete_colormap_entry (GimpImage *image,
|
||||
gint color_index,
|
||||
gboolean push_undo)
|
||||
{
|
||||
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
|
||||
|
||||
if (! gimp_image_colormap_is_index_used (image, color_index))
|
||||
{
|
||||
GimpImagePrivate *private;
|
||||
GimpPaletteEntry *entry;
|
||||
GList *layers;
|
||||
GList *iter;
|
||||
|
||||
if (push_undo)
|
||||
{
|
||||
gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP,
|
||||
C_("undo-type", "Delete Colormap entry"));
|
||||
|
||||
gimp_image_undo_push_image_colormap (image, NULL);
|
||||
}
|
||||
|
||||
private = GIMP_IMAGE_GET_PRIVATE (image);
|
||||
layers = gimp_image_get_layer_list (image);
|
||||
|
||||
for (iter = layers; iter; iter = g_list_next (iter))
|
||||
{
|
||||
if (push_undo)
|
||||
gimp_image_undo_push_drawable_mod (image, NULL, iter->data, TRUE);
|
||||
|
||||
gimp_gegl_shift_index (gimp_drawable_get_buffer (iter->data), NULL,
|
||||
gimp_drawable_get_format_without_alpha (iter->data),
|
||||
color_index, -1);
|
||||
}
|
||||
|
||||
entry = gimp_palette_get_entry (private->palette, color_index);
|
||||
gimp_palette_delete_entry (private->palette, entry);
|
||||
|
||||
g_list_free (layers);
|
||||
|
||||
if (push_undo)
|
||||
gimp_image_undo_group_end (image);
|
||||
|
||||
gimp_image_colormap_changed (image, -1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* private functions */
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ void gimp_image_set_colormap (GimpImage *image,
|
|||
void gimp_image_unset_colormap (GimpImage *image,
|
||||
gboolean push_undo);
|
||||
|
||||
gboolean gimp_image_colormap_is_index_used (GimpImage *image,
|
||||
gint color_index);
|
||||
|
||||
void gimp_image_get_colormap_entry (GimpImage *image,
|
||||
gint color_index,
|
||||
GimpRGB *color);
|
||||
|
|
@ -55,6 +58,9 @@ void gimp_image_set_colormap_entry (GimpImage *image,
|
|||
|
||||
void gimp_image_add_colormap_entry (GimpImage *image,
|
||||
const GimpRGB *color);
|
||||
gboolean gimp_image_delete_colormap_entry (GimpImage *image,
|
||||
gint color_index,
|
||||
gboolean push_undo);
|
||||
|
||||
|
||||
#endif /* __GIMP_IMAGE_COLORMAP_H__ */
|
||||
|
|
|
|||
|
|
@ -621,6 +621,9 @@ gimp_image_undo_dirty_from_type (GimpUndoType undo_type)
|
|||
case GIMP_UNDO_GROUP_IMAGE_CONVERT:
|
||||
return GIMP_DIRTY_IMAGE | GIMP_DIRTY_DRAWABLE;
|
||||
|
||||
case GIMP_UNDO_GROUP_IMAGE_COLORMAP_REMAP:
|
||||
return GIMP_DIRTY_IMAGE | GIMP_DIRTY_DRAWABLE;
|
||||
|
||||
case GIMP_UNDO_GROUP_IMAGE_LAYERS_MERGE:
|
||||
return GIMP_DIRTY_IMAGE_STRUCTURE | GIMP_DIRTY_DRAWABLE;
|
||||
|
||||
|
|
|
|||
|
|
@ -914,6 +914,120 @@ gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer,
|
|||
});
|
||||
}
|
||||
|
||||
gboolean
|
||||
gimp_gegl_is_index_used (GeglBuffer *indexed_buffer,
|
||||
const GeglRectangle *indexed_rect,
|
||||
const Babl *indexed_format,
|
||||
gint index)
|
||||
{
|
||||
GRWLock lock;
|
||||
gboolean found = FALSE;
|
||||
|
||||
g_rw_lock_init (&lock);
|
||||
|
||||
if (! indexed_rect)
|
||||
indexed_rect = gegl_buffer_get_extent (indexed_buffer);
|
||||
|
||||
gegl_parallel_distribute_area (
|
||||
indexed_rect, PIXELS_PER_THREAD,
|
||||
[&] (const GeglRectangle *indexed_area)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0,
|
||||
indexed_format,
|
||||
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
const guchar *indexed = (const guchar *) iter->items[0].data;
|
||||
gint count = iter->length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (*indexed == index)
|
||||
{
|
||||
/*
|
||||
* Position of one item using this color index:
|
||||
gint x = iter->items[0].roi.x + (iter->length - count - 1) % iter->items[0].roi.width;
|
||||
gint y = iter->items[0].roi.y + (gint) ((iter->length - count - 1) / iter->items[0].roi.width);
|
||||
*/
|
||||
g_rw_lock_writer_lock (&lock);
|
||||
found = TRUE;
|
||||
g_rw_lock_writer_unlock (&lock);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_rw_lock_reader_lock (&lock);
|
||||
if (found)
|
||||
{
|
||||
g_rw_lock_reader_unlock (&lock);
|
||||
break;
|
||||
}
|
||||
g_rw_lock_reader_unlock (&lock);
|
||||
}
|
||||
|
||||
indexed++;
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
gegl_buffer_iterator_stop (iter);
|
||||
break;
|
||||
}
|
||||
|
||||
g_rw_lock_reader_lock (&lock);
|
||||
if (found)
|
||||
{
|
||||
g_rw_lock_reader_unlock (&lock);
|
||||
gegl_buffer_iterator_stop (iter);
|
||||
break;
|
||||
}
|
||||
g_rw_lock_reader_unlock (&lock);
|
||||
}
|
||||
});
|
||||
g_rw_lock_clear (&lock);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
void
|
||||
gimp_gegl_shift_index (GeglBuffer *indexed_buffer,
|
||||
const GeglRectangle *indexed_rect,
|
||||
const Babl *indexed_format,
|
||||
gint from_index,
|
||||
gint shift)
|
||||
{
|
||||
if (! indexed_rect)
|
||||
indexed_rect = gegl_buffer_get_extent (indexed_buffer);
|
||||
|
||||
gegl_parallel_distribute_area (
|
||||
indexed_rect, PIXELS_PER_THREAD,
|
||||
[=] (const GeglRectangle *indexed_area)
|
||||
{
|
||||
GeglBufferIterator *iter;
|
||||
|
||||
iter = gegl_buffer_iterator_new (indexed_buffer, indexed_area, 0,
|
||||
indexed_format,
|
||||
GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE, 1);
|
||||
|
||||
while (gegl_buffer_iterator_next (iter))
|
||||
{
|
||||
guchar *indexed = (guchar *) iter->items[0].data;
|
||||
gint count = iter->length;
|
||||
|
||||
while (count--)
|
||||
{
|
||||
if (*indexed >= from_index)
|
||||
*indexed += shift;
|
||||
|
||||
indexed++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void
|
||||
gimp_gegl_convert_color_profile_progress (GimpProgress *progress,
|
||||
gdouble value)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,15 @@ void gimp_gegl_index_to_mask (GeglBuffer *indexed_buffer
|
|||
GeglBuffer *mask_buffer,
|
||||
const GeglRectangle *mask_rect,
|
||||
gint index);
|
||||
gboolean gimp_gegl_is_index_used (GeglBuffer *indexed_buffer,
|
||||
const GeglRectangle *indexed_rect,
|
||||
const Babl *indexed_format,
|
||||
gint index);
|
||||
void gimp_gegl_shift_index (GeglBuffer *indexed_buffer,
|
||||
const GeglRectangle *indexed_rect,
|
||||
const Babl *indexed_format,
|
||||
gint from_index,
|
||||
gint shift);
|
||||
|
||||
void gimp_gegl_convert_color_profile (GeglBuffer *src_buffer,
|
||||
const GeglRectangle *src_rect,
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#include "core/gimp.h"
|
||||
#include "core/gimpcontext.h"
|
||||
#include "core/gimpdatafactory.h"
|
||||
#include "core/gimpimage-colormap.h"
|
||||
#include "core/gimppalette.h"
|
||||
#include "core/gimpparamspecs.h"
|
||||
|
||||
|
|
@ -304,9 +305,18 @@ palette_delete_entry_invoker (GimpProcedure *procedure,
|
|||
GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
|
||||
|
||||
if (entry)
|
||||
gimp_palette_delete_entry (palette, entry);
|
||||
{
|
||||
GimpImage *image = gimp_data_get_image (GIMP_DATA (palette));
|
||||
|
||||
if (image != NULL)
|
||||
success = gimp_image_delete_colormap_entry (image, entry_num, TRUE);
|
||||
else
|
||||
gimp_palette_delete_entry (palette, entry);
|
||||
}
|
||||
else
|
||||
success = FALSE;
|
||||
{
|
||||
success = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
success = FALSE;
|
||||
|
|
@ -683,7 +693,8 @@ register_palette_procs (GimpPDB *pdb)
|
|||
"gimp-palette-delete-entry");
|
||||
gimp_procedure_set_static_help (procedure,
|
||||
"Deletes an entry from the palette.",
|
||||
"Deletes an entry from the palette. Returns an error if the index is out or range. Returns an error if the palette is not editable.",
|
||||
"This function will fail and return %FALSE if the index is out or range or if the palette is not editable.\n"
|
||||
"Additionally if the palette belongs to an indexed image, it will only be possible to delete palette colors not in use in the image.",
|
||||
NULL);
|
||||
gimp_procedure_set_static_attribution (procedure,
|
||||
"Michael Natterer <mitch@gimp.org>",
|
||||
|
|
|
|||
|
|
@ -326,8 +326,10 @@ gimp_palette_add_entry (GimpPalette *palette,
|
|||
*
|
||||
* Deletes an entry from the palette.
|
||||
*
|
||||
* Deletes an entry from the palette. Returns an error if the index is
|
||||
* out or range. Returns an error if the palette is not editable.
|
||||
* This function will fail and return %FALSE if the index is out or
|
||||
* range or if the palette is not editable.
|
||||
* Additionally if the palette belongs to an indexed image, it will
|
||||
* only be possible to delete palette colors not in use in the image.
|
||||
*
|
||||
* Returns: TRUE on success.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -251,9 +251,11 @@ sub palette_delete_entry {
|
|||
$blurb = 'Deletes an entry from the palette.';
|
||||
|
||||
$help = <<'HELP';
|
||||
Deletes an entry from the palette.
|
||||
Returns an error if the index is out or range.
|
||||
Returns an error if the palette is not editable.
|
||||
This function will fail and return %FALSE if the index is out or range or if the
|
||||
palette is not editable.
|
||||
|
||||
Additionally if the palette belongs to an indexed image, it will only be possible
|
||||
to delete palette colors not in use in the image.
|
||||
HELP
|
||||
|
||||
&mitch_pdb_misc('2004', '2.2');
|
||||
|
|
@ -272,9 +274,18 @@ HELP
|
|||
GimpPaletteEntry *entry = gimp_palette_get_entry (palette, entry_num);
|
||||
|
||||
if (entry)
|
||||
gimp_palette_delete_entry (palette, entry);
|
||||
{
|
||||
GimpImage *image = gimp_data_get_image (GIMP_DATA (palette));
|
||||
|
||||
if (image != NULL)
|
||||
success = gimp_image_delete_colormap_entry (image, entry_num, TRUE);
|
||||
else
|
||||
gimp_palette_delete_entry (palette, entry);
|
||||
}
|
||||
else
|
||||
success = FALSE;
|
||||
{
|
||||
success = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
success = FALSE;
|
||||
|
|
@ -420,6 +431,7 @@ CODE
|
|||
"core/gimp.h"
|
||||
"core/gimpcontext.h"
|
||||
"core/gimpdatafactory.h"
|
||||
"core/gimpimage-colormap.h"
|
||||
"core/gimppalette.h"
|
||||
"gimppdb-utils.h");
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue