diff --git a/app/core/gimphistogram.c b/app/core/gimphistogram.c index 2d67bf829d..730484d037 100644 --- a/app/core/gimphistogram.c +++ b/app/core/gimphistogram.c @@ -37,6 +37,7 @@ #include "gimp-atomic.h" #include "gimp-parallel.h" #include "gimpasync.h" +#include "gimpdrawable.h" #include "gimphistogram.h" #include "gimpwaitable.h" @@ -119,6 +120,9 @@ static void gimp_histogram_calculate_area (const GeglRectangle CalculateData *data); static void gimp_histogram_calculate_async_callback (GimpAsync *async, 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) @@ -229,6 +233,49 @@ gimp_histogram_get_memsize (GimpObject *object, 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 */ GimpHistogram * @@ -1230,3 +1277,52 @@ gimp_histogram_calculate_async_callback (GimpAsync *async, 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; +} \ No newline at end of file diff --git a/app/core/gimphistogram.h b/app/core/gimphistogram.h index 56a943d490..10ce299aa8 100644 --- a/app/core/gimphistogram.h +++ b/app/core/gimphistogram.h @@ -100,6 +100,7 @@ gint gimp_histogram_n_components (GimpHistogram *histogram) gint gimp_histogram_n_bins (GimpHistogram *histogram); gboolean gimp_histogram_has_channel (GimpHistogram *histogram, GimpHistogramChannel channel); +guint gimp_histogram_unique_colors (GimpDrawable *drawable); #endif /* __GIMP_HISTOGRAM_H__ */ diff --git a/app/widgets/gimphistogrameditor.c b/app/widgets/gimphistogrameditor.c index 8dd6aa9216..09462bc672 100644 --- a/app/widgets/gimphistogrameditor.c +++ b/app/widgets/gimphistogrameditor.c @@ -132,12 +132,12 @@ gimp_histogram_editor_init (GimpHistogramEditor *editor) const gchar *gimp_histogram_editor_labels[] = { - N_("Mean:"), - N_("Std dev:"), - N_("Median:"), - N_("Pixels:"), - N_("Count:"), - N_("Percentile:") + N_("Mean: "), + N_("Std dev: "), + N_("Median: "), + N_("Pixels: "), + N_("Count: "), + N_("Percentile: ") }; editor->box = gimp_histogram_box_new (); @@ -221,6 +221,30 @@ gimp_histogram_editor_init (GimpHistogramEditor *editor) gtk_grid_attach (GTK_GRID (grid), label, x + 1, y, 1, 1); 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 @@ -767,12 +791,23 @@ gimp_histogram_editor_info_update (GimpHistogramEditor *editor) (100.0 * count / pixels) : 0.0)); 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 { gint i; - for (i = 0; i < 6; i++) + for (i = 0; i < 7; i++) gtk_label_set_text (GTK_LABEL (editor->labels[i]), NULL); } } diff --git a/app/widgets/gimphistogrameditor.h b/app/widgets/gimphistogrameditor.h index bed27cfd85..cd3fb2fa6e 100644 --- a/app/widgets/gimphistogrameditor.h +++ b/app/widgets/gimphistogrameditor.h @@ -51,7 +51,8 @@ struct _GimpHistogramEditor GtkWidget *menu; GtkWidget *box; - GtkWidget *labels[6]; + GtkWidget *labels[7]; + GtkWidget *toggle; }; struct _GimpHistogramEditorClass