summaryrefslogtreecommitdiffstats
path: root/app/core/gimppalette.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /app/core/gimppalette.c
parentInitial commit. (diff)
downloadgimp-e42129241681dde7adae7d20697e7b421682fbb4.tar.xz
gimp-e42129241681dde7adae7d20697e7b421682fbb4.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimppalette.c')
-rw-r--r--app/core/gimppalette.c721
1 files changed, 721 insertions, 0 deletions
diff --git a/app/core/gimppalette.c b/app/core/gimppalette.c
new file mode 100644
index 0000000..f5b848c
--- /dev/null
+++ b/app/core/gimppalette.c
@@ -0,0 +1,721 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <cairo.h>
+#include <gegl.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "libgimpbase/gimpbase.h"
+#include "libgimpcolor/gimpcolor.h"
+
+#include "core-types.h"
+
+#include "gimp-memsize.h"
+#include "gimppalette.h"
+#include "gimppalette-load.h"
+#include "gimppalette-save.h"
+#include "gimptagged.h"
+#include "gimptempbuf.h"
+
+#include "gimp-intl.h"
+
+
+#define RGB_EPSILON 1e-6
+
+
+/* local function prototypes */
+
+static void gimp_palette_tagged_iface_init (GimpTaggedInterface *iface);
+
+static void gimp_palette_finalize (GObject *object);
+
+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_entry_free (GimpPaletteEntry *entry);
+static gint64 gimp_palette_entry_get_memsize (GimpPaletteEntry *entry,
+ gint64 *gui_size);
+static gchar * gimp_palette_get_checksum (GimpTagged *tagged);
+
+
+G_DEFINE_TYPE_WITH_CODE (GimpPalette, gimp_palette, GIMP_TYPE_DATA,
+ G_IMPLEMENT_INTERFACE (GIMP_TYPE_TAGGED,
+ gimp_palette_tagged_iface_init))
+
+#define parent_class gimp_palette_parent_class
+
+
+static void
+gimp_palette_class_init (GimpPaletteClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GimpObjectClass *gimp_object_class = GIMP_OBJECT_CLASS (klass);
+ GimpViewableClass *viewable_class = GIMP_VIEWABLE_CLASS (klass);
+ GimpDataClass *data_class = GIMP_DATA_CLASS (klass);
+
+ object_class->finalize = gimp_palette_finalize;
+
+ gimp_object_class->get_memsize = gimp_palette_get_memsize;
+
+ viewable_class->default_icon_name = "gtk-select-color";
+ viewable_class->get_preview_size = gimp_palette_get_preview_size;
+ viewable_class->get_popup_size = gimp_palette_get_popup_size;
+ viewable_class->get_new_preview = gimp_palette_get_new_preview;
+ viewable_class->get_description = gimp_palette_get_description;
+
+ data_class->save = gimp_palette_save;
+ data_class->get_extension = gimp_palette_get_extension;
+ data_class->copy = gimp_palette_copy;
+}
+
+static void
+gimp_palette_tagged_iface_init (GimpTaggedInterface *iface)
+{
+ iface->get_checksum = gimp_palette_get_checksum;
+}
+
+static void
+gimp_palette_init (GimpPalette *palette)
+{
+ palette->colors = NULL;
+ palette->n_colors = 0;
+ palette->n_columns = 0;
+}
+
+static void
+gimp_palette_finalize (GObject *object)
+{
+ GimpPalette *palette = GIMP_PALETTE (object);
+
+ if (palette->colors)
+ {
+ g_list_free_full (palette->colors,
+ (GDestroyNotify) gimp_palette_entry_free);
+ palette->colors = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint64
+gimp_palette_get_memsize (GimpObject *object,
+ gint64 *gui_size)
+{
+ GimpPalette *palette = GIMP_PALETTE (object);
+ gint64 memsize = 0;
+
+ memsize += gimp_g_list_get_memsize_foreach (palette->colors,
+ (GimpMemsizeFunc)
+ gimp_palette_entry_get_memsize,
+ gui_size);
+
+ return memsize + GIMP_OBJECT_CLASS (parent_class)->get_memsize (object,
+ gui_size);
+}
+
+static void
+gimp_palette_get_preview_size (GimpViewable *viewable,
+ gint size,
+ gboolean popup,
+ gboolean dot_for_dot,
+ gint *width,
+ gint *height)
+{
+ *width = size;
+ *height = 1 + size / 2;
+}
+
+static gboolean
+gimp_palette_get_popup_size (GimpViewable *viewable,
+ gint width,
+ gint height,
+ gboolean dot_for_dot,
+ gint *popup_width,
+ gint *popup_height)
+{
+ GimpPalette *palette = GIMP_PALETTE (viewable);
+ gint p_width;
+ gint p_height;
+
+ if (! palette->n_colors)
+ return FALSE;
+
+ if (palette->n_columns)
+ p_width = palette->n_columns;
+ else
+ p_width = MIN (palette->n_colors, 16);
+
+ p_height = MAX (1, palette->n_colors / p_width);
+
+ if (p_width * 4 > width || p_height * 4 > height)
+ {
+ *popup_width = p_width * 4;
+ *popup_height = p_height * 4;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static GimpTempBuf *
+gimp_palette_get_new_preview (GimpViewable *viewable,
+ GimpContext *context,
+ gint width,
+ gint height)
+{
+ GimpPalette *palette = GIMP_PALETTE (viewable);
+ GimpTempBuf *temp_buf;
+ guchar *buf;
+ guchar *b;
+ GList *list;
+ gint columns;
+ gint rows;
+ gint cell_size;
+ gint x, y;
+
+ temp_buf = gimp_temp_buf_new (width, height, babl_format ("R'G'B' u8"));
+ memset (gimp_temp_buf_get_data (temp_buf), 255, width * height * 3);
+
+ if (palette->n_columns > 1)
+ cell_size = MAX (4, width / palette->n_columns);
+ else
+ cell_size = 4;
+
+ columns = width / cell_size;
+ rows = height / cell_size;
+
+ buf = gimp_temp_buf_get_data (temp_buf);
+ b = g_new (guchar, width * 3);
+
+ list = palette->colors;
+
+ for (y = 0; y < rows && list; y++)
+ {
+ gint i;
+
+ memset (b, 255, width * 3);
+
+ for (x = 0; x < columns && list; x++)
+ {
+ GimpPaletteEntry *entry = list->data;
+
+ list = g_list_next (list);
+
+ gimp_rgb_get_uchar (&entry->color,
+ &b[x * cell_size * 3 + 0],
+ &b[x * cell_size * 3 + 1],
+ &b[x * cell_size * 3 + 2]);
+
+ for (i = 1; i < cell_size; i++)
+ {
+ b[(x * cell_size + i) * 3 + 0] = b[(x * cell_size) * 3 + 0];
+ b[(x * cell_size + i) * 3 + 1] = b[(x * cell_size) * 3 + 1];
+ b[(x * cell_size + i) * 3 + 2] = b[(x * cell_size) * 3 + 2];
+ }
+ }
+
+ for (i = 0; i < cell_size; i++)
+ memcpy (buf + ((y * cell_size + i) * width) * 3, b, width * 3);
+ }
+
+ g_free (b);
+
+ return temp_buf;
+}
+
+static gchar *
+gimp_palette_get_description (GimpViewable *viewable,
+ gchar **tooltip)
+{
+ GimpPalette *palette = GIMP_PALETTE (viewable);
+
+ return g_strdup_printf ("%s (%d)",
+ gimp_object_get_name (palette),
+ palette->n_colors);
+}
+
+GimpData *
+gimp_palette_new (GimpContext *context,
+ const gchar *name)
+{
+ g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (*name != '\0', NULL);
+
+ return g_object_new (GIMP_TYPE_PALETTE,
+ "name", name,
+ NULL);
+}
+
+GimpData *
+gimp_palette_get_standard (GimpContext *context)
+{
+ static GimpData *standard_palette = NULL;
+
+ if (! standard_palette)
+ {
+ standard_palette = gimp_palette_new (context, "Standard");
+
+ gimp_data_clean (standard_palette);
+ gimp_data_make_internal (standard_palette, "gimp-palette-standard");
+
+ g_object_add_weak_pointer (G_OBJECT (standard_palette),
+ (gpointer *) &standard_palette);
+ }
+
+ return standard_palette;
+}
+
+static const gchar *
+gimp_palette_get_extension (GimpData *data)
+{
+ return GIMP_PALETTE_FILE_EXTENSION;
+}
+
+static void
+gimp_palette_copy (GimpData *data,
+ GimpData *src_data)
+{
+ GimpPalette *palette = GIMP_PALETTE (data);
+ GimpPalette *src_palette = GIMP_PALETTE (src_data);
+ GList *list;
+
+ gimp_data_freeze (data);
+
+ if (palette->colors)
+ {
+ g_list_free_full (palette->colors,
+ (GDestroyNotify) gimp_palette_entry_free);
+ palette->colors = NULL;
+ }
+
+ palette->n_colors = 0;
+ palette->n_columns = src_palette->n_columns;
+
+ for (list = src_palette->colors; list; list = g_list_next (list))
+ {
+ GimpPaletteEntry *entry = list->data;
+
+ gimp_palette_add_entry (palette, -1, entry->name, &entry->color);
+ }
+
+ gimp_data_thaw (data);
+}
+
+static gchar *
+gimp_palette_get_checksum (GimpTagged *tagged)
+{
+ GimpPalette *palette = GIMP_PALETTE (tagged);
+ gchar *checksum_string = NULL;
+
+ if (palette->n_colors > 0)
+ {
+ GChecksum *checksum = g_checksum_new (G_CHECKSUM_MD5);
+ GList *color_iterator = palette->colors;
+
+ g_checksum_update (checksum, (const guchar *) &palette->n_colors, sizeof (palette->n_colors));
+ g_checksum_update (checksum, (const guchar *) &palette->n_columns, sizeof (palette->n_columns));
+
+ while (color_iterator)
+ {
+ GimpPaletteEntry *entry = (GimpPaletteEntry *) color_iterator->data;
+
+ g_checksum_update (checksum, (const guchar *) &entry->color, sizeof (entry->color));
+ if (entry->name)
+ g_checksum_update (checksum, (const guchar *) entry->name, strlen (entry->name));
+
+ color_iterator = g_list_next (color_iterator);
+ }
+
+ checksum_string = g_strdup (g_checksum_get_string (checksum));
+
+ g_checksum_free (checksum);
+ }
+
+ return checksum_string;
+}
+
+
+/* public functions */
+
+GList *
+gimp_palette_get_colors (GimpPalette *palette)
+{
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL);
+
+ return palette->colors;
+}
+
+gint
+gimp_palette_get_n_colors (GimpPalette *palette)
+{
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), 0);
+
+ return palette->n_colors;
+}
+
+void
+gimp_palette_move_entry (GimpPalette *palette,
+ GimpPaletteEntry *entry,
+ gint position)
+{
+ GList *list;
+ gint pos = 0;
+
+ g_return_if_fail (GIMP_IS_PALETTE (palette));
+ g_return_if_fail (entry != NULL);
+
+ if (g_list_find (palette->colors, entry))
+ {
+ pos = entry->position;
+
+ if (entry->position == position)
+ return;
+
+ entry->position = position;
+ palette->colors = g_list_remove (palette->colors,
+ entry);
+ palette->colors = g_list_insert (palette->colors,
+ entry, position);
+
+ if (pos < position)
+ {
+ for (list = g_list_nth (palette->colors, pos);
+ list && pos < position;
+ list = g_list_next (list))
+ {
+ entry = (GimpPaletteEntry *) list->data;
+
+ entry->position = pos++;
+ }
+ }
+ else
+ {
+ for (list = g_list_nth (palette->colors, position + 1);
+ list && position < pos;
+ list = g_list_next (list))
+ {
+ entry = (GimpPaletteEntry *) list->data;
+
+ entry->position += 1;
+ pos--;
+ }
+ }
+
+ gimp_data_dirty (GIMP_DATA (palette));
+ }
+}
+
+GimpPaletteEntry *
+gimp_palette_add_entry (GimpPalette *palette,
+ gint position,
+ const gchar *name,
+ const GimpRGB *color)
+{
+ GimpPaletteEntry *entry;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL);
+ g_return_val_if_fail (color != NULL, NULL);
+
+ entry = g_slice_new0 (GimpPaletteEntry);
+
+ entry->color = *color;
+ entry->name = g_strdup (name ? name : _("Untitled"));
+
+ if (position < 0 || position >= palette->n_colors)
+ {
+ entry->position = palette->n_colors;
+ palette->colors = g_list_append (palette->colors, entry);
+ }
+ else
+ {
+ GList *list;
+
+ entry->position = position;
+ palette->colors = g_list_insert (palette->colors, entry, position);
+
+ /* renumber the displaced entries */
+ for (list = g_list_nth (palette->colors, position + 1);
+ list;
+ list = g_list_next (list))
+ {
+ GimpPaletteEntry *entry2 = list->data;
+
+ entry2->position += 1;
+ }
+ }
+
+ palette->n_colors += 1;
+
+ gimp_data_dirty (GIMP_DATA (palette));
+
+ return entry;
+}
+
+void
+gimp_palette_delete_entry (GimpPalette *palette,
+ GimpPaletteEntry *entry)
+{
+ GList *list;
+ gint pos = 0;
+
+ g_return_if_fail (GIMP_IS_PALETTE (palette));
+ g_return_if_fail (entry != NULL);
+
+ if (g_list_find (palette->colors, entry))
+ {
+ pos = entry->position;
+ gimp_palette_entry_free (entry);
+
+ palette->colors = g_list_remove (palette->colors, entry);
+
+ palette->n_colors--;
+
+ for (list = g_list_nth (palette->colors, pos);
+ list;
+ list = g_list_next (list))
+ {
+ entry = (GimpPaletteEntry *) list->data;
+
+ entry->position = pos++;
+ }
+
+ gimp_data_dirty (GIMP_DATA (palette));
+ }
+}
+
+gboolean
+gimp_palette_set_entry (GimpPalette *palette,
+ gint position,
+ const gchar *name,
+ const GimpRGB *color)
+{
+ GimpPaletteEntry *entry;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+
+ entry = gimp_palette_get_entry (palette, position);
+
+ if (! entry)
+ return FALSE;
+
+ entry->color = *color;
+
+ if (entry->name)
+ g_free (entry->name);
+
+ entry->name = g_strdup (name);
+
+ gimp_data_dirty (GIMP_DATA (palette));
+
+ return TRUE;
+}
+
+gboolean
+gimp_palette_set_entry_color (GimpPalette *palette,
+ gint position,
+ const GimpRGB *color)
+{
+ GimpPaletteEntry *entry;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE);
+ g_return_val_if_fail (color != NULL, FALSE);
+
+ entry = gimp_palette_get_entry (palette, position);
+
+ if (! entry)
+ return FALSE;
+
+ entry->color = *color;
+
+ gimp_data_dirty (GIMP_DATA (palette));
+
+ return TRUE;
+}
+
+gboolean
+gimp_palette_set_entry_name (GimpPalette *palette,
+ gint position,
+ const gchar *name)
+{
+ GimpPaletteEntry *entry;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), FALSE);
+
+ entry = gimp_palette_get_entry (palette, position);
+
+ if (! entry)
+ return FALSE;
+
+ if (entry->name)
+ g_free (entry->name);
+
+ entry->name = g_strdup (name);
+
+ gimp_data_dirty (GIMP_DATA (palette));
+
+ return TRUE;
+}
+
+GimpPaletteEntry *
+gimp_palette_get_entry (GimpPalette *palette,
+ gint position)
+{
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL);
+
+ return g_list_nth_data (palette->colors, position);
+}
+
+void
+gimp_palette_set_columns (GimpPalette *palette,
+ gint columns)
+{
+ g_return_if_fail (GIMP_IS_PALETTE (palette));
+
+ columns = CLAMP (columns, 0, 64);
+
+ if (palette->n_columns != columns)
+ {
+ palette->n_columns = columns;
+
+ gimp_data_dirty (GIMP_DATA (palette));
+ }
+}
+
+gint
+gimp_palette_get_columns (GimpPalette *palette)
+{
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), 0);
+
+ return palette->n_columns;
+}
+
+GimpPaletteEntry *
+gimp_palette_find_entry (GimpPalette *palette,
+ const GimpRGB *color,
+ GimpPaletteEntry *start_from)
+{
+ GimpPaletteEntry *entry;
+
+ g_return_val_if_fail (GIMP_IS_PALETTE (palette), NULL);
+ g_return_val_if_fail (color != NULL, NULL);
+
+ if (! start_from)
+ {
+ GList *list;
+
+ /* search from the start */
+
+ for (list = palette->colors; list; list = g_list_next (list))
+ {
+ entry = (GimpPaletteEntry *) list->data;
+ if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON)
+ return entry;
+ }
+ }
+ else if (gimp_rgb_distance (&start_from->color, color) < RGB_EPSILON)
+ {
+ return start_from;
+ }
+ else
+ {
+ GList *old = g_list_find (palette->colors, start_from);
+ GList *next;
+ GList *prev;
+
+ g_return_val_if_fail (old != NULL, NULL);
+
+ next = old->next;
+ prev = old->prev;
+
+ /* proximity-based search */
+
+ while (next || prev)
+ {
+ if (next)
+ {
+ entry = (GimpPaletteEntry *) next->data;
+ if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON)
+ return entry;
+
+ next = next->next;
+ }
+
+ if (prev)
+ {
+ entry = (GimpPaletteEntry *) prev->data;
+ if (gimp_rgb_distance (&entry->color, color) < RGB_EPSILON)
+ return entry;
+
+ prev = prev->prev;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+/* private functions */
+
+static void
+gimp_palette_entry_free (GimpPaletteEntry *entry)
+{
+ g_return_if_fail (entry != NULL);
+
+ g_free (entry->name);
+
+ g_slice_free (GimpPaletteEntry, entry);
+}
+
+static gint64
+gimp_palette_entry_get_memsize (GimpPaletteEntry *entry,
+ gint64 *gui_size)
+{
+ gint64 memsize = sizeof (GimpPaletteEntry);
+
+ memsize += gimp_string_get_memsize (entry->name);
+
+ return memsize;
+}