diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:30:19 +0000 |
commit | 5c1676dfe6d2f3c837a5e074117b45613fd29a72 (patch) | |
tree | cbffb45144febf451e54061db2b21395faf94bfe /app/core/gimppalette-import.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.34.upstream/2.10.34upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'app/core/gimppalette-import.c')
-rw-r--r-- | app/core/gimppalette-import.c | 566 |
1 files changed, 566 insertions, 0 deletions
diff --git a/app/core/gimppalette-import.c b/app/core/gimppalette-import.c new file mode 100644 index 0000000..aaa44ea --- /dev/null +++ b/app/core/gimppalette-import.c @@ -0,0 +1,566 @@ +/* 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 <cairo.h> +#include <gegl.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpcolor/gimpcolor.h" + +#include "core-types.h" + +#include "gimpchannel.h" +#include "gimpcontainer.h" +#include "gimpcontext.h" +#include "gimpgradient.h" +#include "gimpimage.h" +#include "gimpimage-colormap.h" +#include "gimppalette.h" +#include "gimppalette-import.h" +#include "gimppalette-load.h" +#include "gimppickable.h" + +#include "gimp-intl.h" + + +#define MAX_IMAGE_COLORS (10000 * 2) + + +/* create a palette from a gradient ****************************************/ + +GimpPalette * +gimp_palette_import_from_gradient (GimpGradient *gradient, + GimpContext *context, + gboolean reverse, + GimpGradientBlendColorSpace blend_color_space, + const gchar *palette_name, + gint n_colors) +{ + GimpPalette *palette; + GimpGradientSegment *seg = NULL; + gdouble dx, cur_x; + GimpRGB color; + gint i; + + g_return_val_if_fail (GIMP_IS_GRADIENT (gradient), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + dx = 1.0 / (n_colors - 1); + + for (i = 0, cur_x = 0; i < n_colors; i++, cur_x += dx) + { + seg = gimp_gradient_get_color_at (gradient, context, + seg, cur_x, reverse, blend_color_space, + &color); + gimp_palette_add_entry (palette, -1, NULL, &color); + } + + return palette; +} + + +/* create a palette from a non-indexed image *******************************/ + +typedef struct _ImgColors ImgColors; + +struct _ImgColors +{ + guint count; + guint r_adj; + guint g_adj; + guint b_adj; + guchar r; + guchar g; + guchar b; +}; + +static gint count_color_entries = 0; + +static GHashTable * +gimp_palette_import_store_colors (GHashTable *table, + guchar *colors, + guchar *colors_real, + gint n_colors) +{ + gpointer found_color = NULL; + ImgColors *new_color; + guint key_colors = colors[0] * 256 * 256 + colors[1] * 256 + colors[2]; + + if (table == NULL) + { + table = g_hash_table_new (g_direct_hash, g_direct_equal); + count_color_entries = 0; + } + else + { + found_color = g_hash_table_lookup (table, GUINT_TO_POINTER (key_colors)); + } + + if (found_color == NULL) + { + if (count_color_entries > MAX_IMAGE_COLORS) + { + /* Don't add any more new ones */ + return table; + } + + count_color_entries++; + + new_color = g_slice_new (ImgColors); + + new_color->count = 1; + new_color->r_adj = 0; + new_color->g_adj = 0; + new_color->b_adj = 0; + new_color->r = colors[0]; + new_color->g = colors[1]; + new_color->b = colors[2]; + + g_hash_table_insert (table, GUINT_TO_POINTER (key_colors), new_color); + } + else + { + new_color = found_color; + + if (new_color->count < (G_MAXINT - 1)) + new_color->count++; + + /* Now do the adjustments ...*/ + new_color->r_adj += (colors_real[0] - colors[0]); + new_color->g_adj += (colors_real[1] - colors[1]); + new_color->b_adj += (colors_real[2] - colors[2]); + + /* Boundary conditions */ + if(new_color->r_adj > (G_MAXINT - 255)) + new_color->r_adj /= new_color->count; + + if(new_color->g_adj > (G_MAXINT - 255)) + new_color->g_adj /= new_color->count; + + if(new_color->b_adj > (G_MAXINT - 255)) + new_color->b_adj /= new_color->count; + } + + return table; +} + +static void +gimp_palette_import_create_list (gpointer key, + gpointer value, + gpointer user_data) +{ + GSList **list = user_data; + ImgColors *color_tab = value; + + *list = g_slist_prepend (*list, color_tab); +} + +static gint +gimp_palette_import_sort_colors (gconstpointer a, + gconstpointer b) +{ + const ImgColors *s1 = a; + const ImgColors *s2 = b; + + if(s1->count > s2->count) + return -1; + if(s1->count < s2->count) + return 1; + + return 0; +} + +static void +gimp_palette_import_create_image_palette (gpointer data, + gpointer user_data) +{ + GimpPalette *palette = user_data; + ImgColors *color_tab = data; + gint n_colors; + gchar *lab; + GimpRGB color; + + n_colors = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (palette), + "import-n-colors")); + + if (gimp_palette_get_n_colors (palette) >= n_colors) + return; + + /* TRANSLATORS: the "%s" is an item title and "%u" is the number of + occurrences for this item. */ + lab = g_strdup_printf (_("%s (occurs %u)"), + _("Untitled"), + color_tab->count); + + /* Adjust the colors to the mean of the the sample */ + gimp_rgba_set_uchar + (&color, + (guchar) color_tab->r + (color_tab->r_adj / color_tab->count), + (guchar) color_tab->g + (color_tab->g_adj / color_tab->count), + (guchar) color_tab->b + (color_tab->b_adj / color_tab->count), + 255); + + gimp_palette_add_entry (palette, -1, lab, &color); + + g_free (lab); +} + +static GimpPalette * +gimp_palette_import_make_palette (GHashTable *table, + const gchar *palette_name, + GimpContext *context, + gint n_colors) +{ + GimpPalette *palette; + GSList *list = NULL; + GSList *iter; + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + if (! table) + return palette; + + g_hash_table_foreach (table, gimp_palette_import_create_list, &list); + list = g_slist_sort (list, gimp_palette_import_sort_colors); + + g_object_set_data (G_OBJECT (palette), "import-n-colors", + GINT_TO_POINTER (n_colors)); + + g_slist_foreach (list, gimp_palette_import_create_image_palette, palette); + + g_object_set_data (G_OBJECT (palette), "import-n-colors", NULL); + + /* Free up used memory + * Note the same structure is on both the hash list and the sorted + * list. So only delete it once. + */ + g_hash_table_destroy (table); + + for (iter = list; iter; iter = iter->next) + g_slice_free (ImgColors, iter->data); + + g_slist_free (list); + + return palette; +} + +static GHashTable * +gimp_palette_import_extract (GimpImage *image, + GimpPickable *pickable, + gint pickable_off_x, + gint pickable_off_y, + gboolean selection_only, + gint x, + gint y, + gint width, + gint height, + gint n_colors, + gint threshold) +{ + GeglBuffer *buffer; + GeglBufferIterator *iter; + GeglRectangle *mask_roi = NULL; + GeglRectangle rect = { x, y, width, height }; + GHashTable *colors = NULL; + const Babl *format; + gint bpp; + gint mask_bpp = 0; + + buffer = gimp_pickable_get_buffer (pickable); + format = babl_format ("R'G'B'A u8"); + + iter = gegl_buffer_iterator_new (buffer, &rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 2); + bpp = babl_format_get_bytes_per_pixel (format); + + if (selection_only && + ! gimp_channel_is_empty (gimp_image_get_mask (image))) + { + GimpDrawable *mask = GIMP_DRAWABLE (gimp_image_get_mask (image)); + + rect.x = x + pickable_off_x; + rect.y = y + pickable_off_y; + + buffer = gimp_drawable_get_buffer (mask); + format = babl_format ("Y u8"); + + gegl_buffer_iterator_add (iter, buffer, &rect, 0, format, + GEGL_ACCESS_READ, GEGL_ABYSS_NONE); + mask_roi = &iter->items[1].roi; + mask_bpp = babl_format_get_bytes_per_pixel (format); + } + + while (gegl_buffer_iterator_next (iter)) + { + const guchar *data = iter->items[0].data; + const guchar *mask_data = NULL; + gint length = iter->length; + + if (mask_roi) + mask_data = iter->items[1].data; + + while (length--) + { + /* ignore unselected, and completely transparent pixels */ + if ((! mask_data || *mask_data) && data[ALPHA]) + { + guchar rgba[MAX_CHANNELS] = { 0, }; + guchar rgb_real[MAX_CHANNELS] = { 0, }; + + memcpy (rgba, data, 4); + memcpy (rgb_real, rgba, 4); + + rgba[0] = (rgba[0] / threshold) * threshold; + rgba[1] = (rgba[1] / threshold) * threshold; + rgba[2] = (rgba[2] / threshold) * threshold; + + colors = gimp_palette_import_store_colors (colors, + rgba, rgb_real, + n_colors); + } + + data += bpp; + + if (mask_data) + mask_data += mask_bpp; + } + } + + return colors; +} + +GimpPalette * +gimp_palette_import_from_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only) +{ + GHashTable *colors; + gint x, y; + gint width, height; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + g_return_val_if_fail (threshold > 0, NULL); + + gimp_pickable_flush (GIMP_PICKABLE (image)); + + if (selection_only) + { + gimp_item_bounds (GIMP_ITEM (gimp_image_get_mask (image)), + &x, &y, &width, &height); + } + else + { + x = 0; + y = 0; + width = gimp_image_get_width (image); + height = gimp_image_get_height (image); + } + + colors = gimp_palette_import_extract (image, + GIMP_PICKABLE (image), + 0, 0, + selection_only, + x, y, width, height, + n_colors, threshold); + + return gimp_palette_import_make_palette (colors, palette_name, context, + n_colors); +} + + +/* create a palette from an indexed image **********************************/ + +GimpPalette * +gimp_palette_import_from_indexed_image (GimpImage *image, + GimpContext *context, + const gchar *palette_name) +{ + GimpPalette *palette; + const guchar *colormap; + guint n_colors; + gint count; + GimpRGB color; + + g_return_val_if_fail (GIMP_IS_IMAGE (image), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (gimp_image_get_base_type (image) == GIMP_INDEXED, NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + + palette = GIMP_PALETTE (gimp_palette_new (context, palette_name)); + + colormap = gimp_image_get_colormap (image); + n_colors = gimp_image_get_colormap_size (image); + + for (count = 0; count < n_colors; ++count) + { + gchar name[256]; + + g_snprintf (name, sizeof (name), _("Index %d"), count); + + gimp_rgba_set_uchar (&color, + colormap[count * 3 + 0], + colormap[count * 3 + 1], + colormap[count * 3 + 2], + 255); + + gimp_palette_add_entry (palette, -1, name, &color); + } + + return palette; +} + + +/* create a palette from a drawable ****************************************/ + +GimpPalette * +gimp_palette_import_from_drawable (GimpDrawable *drawable, + GimpContext *context, + const gchar *palette_name, + gint n_colors, + gint threshold, + gboolean selection_only) +{ + GHashTable *colors = NULL; + gint x, y; + gint width, height; + gint off_x, off_y; + + g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), NULL); + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (gimp_item_is_attached (GIMP_ITEM (drawable)), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (n_colors > 1, NULL); + g_return_val_if_fail (threshold > 0, NULL); + + if (selection_only) + { + if (! gimp_item_mask_intersect (GIMP_ITEM (drawable), + &x, &y, &width, &height)) + return NULL; + } + else + { + x = 0; + y = 0; + width = gimp_item_get_width (GIMP_ITEM (drawable)); + height = gimp_item_get_height (GIMP_ITEM (drawable)); + } + + gimp_item_get_offset (GIMP_ITEM (drawable), &off_x, &off_y); + + colors = + gimp_palette_import_extract (gimp_item_get_image (GIMP_ITEM (drawable)), + GIMP_PICKABLE (drawable), + off_x, off_y, + selection_only, + x, y, width, height, + n_colors, threshold); + + return gimp_palette_import_make_palette (colors, palette_name, context, + n_colors); +} + + +/* create a palette from a file **********************************/ + +GimpPalette * +gimp_palette_import_from_file (GimpContext *context, + GFile *file, + const gchar *palette_name, + GError **error) +{ + GList *palette_list = NULL; + GInputStream *input; + GError *my_error = NULL; + + g_return_val_if_fail (GIMP_IS_CONTEXT (context), NULL); + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (palette_name != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error)); + if (! input) + { + g_set_error (error, GIMP_DATA_ERROR, GIMP_DATA_ERROR_OPEN, + _("Could not open '%s' for reading: %s"), + gimp_file_get_utf8_name (file), my_error->message); + g_clear_error (&my_error); + return NULL; + } + + switch (gimp_palette_load_detect_format (file, input)) + { + case GIMP_PALETTE_FILE_FORMAT_GPL: + palette_list = gimp_palette_load (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ACT: + palette_list = gimp_palette_load_act (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_RIFF_PAL: + palette_list = gimp_palette_load_riff (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_PSP_PAL: + palette_list = gimp_palette_load_psp (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_ACO: + palette_list = gimp_palette_load_aco (context, file, input, error); + break; + + case GIMP_PALETTE_FILE_FORMAT_CSS: + palette_list = gimp_palette_load_css (context, file, input, error); + break; + + default: + g_set_error (error, + GIMP_DATA_ERROR, GIMP_DATA_ERROR_READ, + _("Unknown type of palette file: %s"), + gimp_file_get_utf8_name (file)); + break; + } + + g_object_unref (input); + + if (palette_list) + { + GimpPalette *palette = g_object_ref (palette_list->data); + + gimp_object_set_name (GIMP_OBJECT (palette), palette_name); + + g_list_free_full (palette_list, (GDestroyNotify) g_object_unref); + + return palette; + } + + return NULL; +} |