widgets: implement unique color count

This patch reintroduce the unique color counting in an image,
an old feature from old color-cube-analyze plug-in.
See #13026
This commit is contained in:
Gabriele Barbero 2025-03-23 20:23:14 +01:00 committed by Alx Sa
parent d028b96654
commit be1496a91e
4 changed files with 141 additions and 8 deletions

View file

@ -37,6 +37,7 @@
#include "gimp-atomic.h" #include "gimp-atomic.h"
#include "gimp-parallel.h" #include "gimp-parallel.h"
#include "gimpasync.h" #include "gimpasync.h"
#include "gimpdrawable.h"
#include "gimphistogram.h" #include "gimphistogram.h"
#include "gimpwaitable.h" #include "gimpwaitable.h"
@ -119,6 +120,9 @@ static void gimp_histogram_calculate_area (const GeglRectangle
CalculateData *data); CalculateData *data);
static void gimp_histogram_calculate_async_callback (GimpAsync *async, static void gimp_histogram_calculate_async_callback (GimpAsync *async,
CalculateContext *context); CalculateContext *context);
static guint hash_color_bytes (gpointer *key);
static gboolean color_bytes_equal (gpointer *key1,
gpointer *key2);
G_DEFINE_TYPE_WITH_PRIVATE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (GimpHistogram, gimp_histogram, GIMP_TYPE_OBJECT)
@ -229,6 +233,49 @@ gimp_histogram_get_memsize (GimpObject *object,
gui_size); gui_size);
} }
static guint
hash_color_bytes (gpointer *key)
{
GBytes *bytes = (GBytes *) key;
gsize size;
const guint8 *data;
guint64 max_value = 0;
guint64 value = 0;
guint hash;
data = g_bytes_get_data (bytes, &size);
for (gsize i = 0; i < size; ++i)
{
value = value * 256 + data[i];
max_value = max_value * 256 + 255;
}
if (max_value == 0)
return 0;
hash = (guint) ((value * (guint64) G_MAXUINT) / max_value);
return hash;
}
static gboolean
color_bytes_equal (gpointer *key1,
gpointer *key2)
{
GBytes *bytes1 = (GBytes *) key1;
GBytes *bytes2 = (GBytes *) key2;
gsize size1;
gsize size2;
const guint8 *data1 = g_bytes_get_data (bytes1, &size1);
const guint8 *data2 = g_bytes_get_data (bytes2, &size2);
if (size1 != size2)
return FALSE;
return memcmp (data1, data2, size1) == 0;
}
/* public functions */ /* public functions */
GimpHistogram * GimpHistogram *
@ -1230,3 +1277,52 @@ gimp_histogram_calculate_async_callback (GimpAsync *async,
g_slice_free (CalculateContext, context); g_slice_free (CalculateContext, context);
} }
guint
gimp_histogram_unique_colors (GimpDrawable *drawable)
{
const Babl *format;
guint bpp;
GeglBufferIterator *iter;
GHashTable *hash_table;
GBytes *key = NULL;
guint uniques = 0;
g_return_val_if_fail (GIMP_IS_DRAWABLE (drawable), 0);
format = gimp_drawable_get_format (drawable);
bpp = babl_format_get_bytes_per_pixel (format);
iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (drawable),
NULL, 0, format,
GEGL_ACCESS_READ, GEGL_ABYSS_NONE, 1);
hash_table = g_hash_table_new ((GHashFunc) hash_color_bytes,
(GEqualFunc) color_bytes_equal);
while (gegl_buffer_iterator_next (iter))
{
guint8 *data = iter->items[0].data;
guint length = iter->length;
while (length--)
{
key = g_bytes_new (data, bpp);
if (! g_hash_table_lookup (hash_table, key))
{
g_hash_table_insert (hash_table, key, key);
key = NULL;
uniques++;
}
data += bpp;
}
}
g_hash_table_destroy (hash_table);
g_bytes_unref (key);
return uniques;
}

View file

@ -100,6 +100,7 @@ gint gimp_histogram_n_components (GimpHistogram *histogram)
gint gimp_histogram_n_bins (GimpHistogram *histogram); gint gimp_histogram_n_bins (GimpHistogram *histogram);
gboolean gimp_histogram_has_channel (GimpHistogram *histogram, gboolean gimp_histogram_has_channel (GimpHistogram *histogram,
GimpHistogramChannel channel); GimpHistogramChannel channel);
guint gimp_histogram_unique_colors (GimpDrawable *drawable);
#endif /* __GIMP_HISTOGRAM_H__ */ #endif /* __GIMP_HISTOGRAM_H__ */

View file

@ -221,6 +221,30 @@ gimp_histogram_editor_init (GimpHistogramEditor *editor)
gtk_grid_attach (GTK_GRID (grid), label, x + 1, y, 1, 1); gtk_grid_attach (GTK_GRID (grid), label, x + 1, y, 1, 1);
gtk_widget_show (label); gtk_widget_show (label);
} }
editor->toggle = gtk_check_button_new_with_label (_("Compute unique colors:"));
gimp_label_set_attributes (GTK_LABEL (gtk_bin_get_child (GTK_BIN (editor->toggle))),
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
-1);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (editor->toggle), FALSE);
g_signal_connect_swapped (editor->toggle, "toggled",
G_CALLBACK (gimp_histogram_editor_info_update),
editor);
gtk_widget_set_margin_start (gtk_bin_get_child (GTK_BIN (editor->toggle)), 6);
gtk_widget_set_halign (editor->toggle, GTK_ALIGN_END);
gtk_grid_attach (GTK_GRID (grid), editor->toggle, 0, 3, 1, 1);
gtk_widget_show (editor->toggle);
editor->labels[6] = label = g_object_new (GTK_TYPE_LABEL,
"xalign", 0.0,
"yalign", 0.5,
"width-chars", 9,
NULL);
gimp_label_set_attributes (GTK_LABEL (label),
PANGO_ATTR_SCALE, PANGO_SCALE_SMALL,
-1);
gtk_grid_attach (GTK_GRID (grid), label, 1, 3, 1, 1);
gtk_widget_show (label);
} }
static void static void
@ -767,12 +791,23 @@ gimp_histogram_editor_info_update (GimpHistogramEditor *editor)
(100.0 * count / pixels) : (100.0 * count / pixels) :
0.0)); 0.0));
gtk_label_set_text (GTK_LABEL (editor->labels[5]), text); gtk_label_set_text (GTK_LABEL (editor->labels[5]), text);
if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (editor->toggle)))
{
g_snprintf (text, sizeof (text), "%d",
gimp_histogram_unique_colors (editor->drawable));
gtk_label_set_text (GTK_LABEL (editor->labels[6]), text);
}
else
{
gtk_label_set_text (GTK_LABEL (editor->labels[6]), "n/a");
}
} }
else else
{ {
gint i; gint i;
for (i = 0; i < 6; i++) for (i = 0; i < 7; i++)
gtk_label_set_text (GTK_LABEL (editor->labels[i]), NULL); gtk_label_set_text (GTK_LABEL (editor->labels[i]), NULL);
} }
} }

View file

@ -51,7 +51,8 @@ struct _GimpHistogramEditor
GtkWidget *menu; GtkWidget *menu;
GtkWidget *box; GtkWidget *box;
GtkWidget *labels[6]; GtkWidget *labels[7];
GtkWidget *toggle;
}; };
struct _GimpHistogramEditorClass struct _GimpHistogramEditorClass