From b98a04aadc91ada5f6b460023d41f2b84ac4da73 Mon Sep 17 00:00:00 2001 From: Ell Date: Fri, 18 May 2018 14:54:15 -0400 Subject: [PATCH] app: add GimpPickable::get_pixel_average() vfunc ... which calculates the average color of the pickable over a given area. Use this function in gimp_pickable_pick_color() when sample_average is TRUE, and provide a default implementation which calculates the average color using GimpPickable::get_pixel_at(), as gimp_pickable_pick_color() did before. The default implementation is rather slow; classes that implement the GimpPickable interface can provide a faster specialized version (see the next commit). --- app/core/gimppickable.c | 116 +++++++++++++++++++++++++++++--------- app/core/gimppickable.h | 120 +++++++++++++++++++++------------------- 2 files changed, 155 insertions(+), 81 deletions(-) diff --git a/app/core/gimppickable.c b/app/core/gimppickable.c index ffe91b771b..7f568b0b8a 100644 --- a/app/core/gimppickable.c +++ b/app/core/gimppickable.c @@ -33,6 +33,7 @@ #include #include "libgimpcolor/gimpcolor.h" +#include "libgimpmath/gimpmath.h" #include "core-types.h" @@ -41,7 +42,17 @@ #include "gimppickable.h" -static void gimp_pickable_interface_base_init (GimpPickableInterface *iface); +/* local function prototypes */ + +static void gimp_pickable_interface_base_init (GimpPickableInterface *iface); + +static void gimp_pickable_real_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); + + +/* private functions */ GType @@ -84,8 +95,54 @@ gimp_pickable_interface_base_init (GimpPickableInterface *iface) initialized = TRUE; } + + if (! iface->get_pixel_average) + iface->get_pixel_average = gimp_pickable_real_get_pixel_average; } +static void +gimp_pickable_real_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + const Babl *average_format = babl_format ("RaGaBaA double"); + gdouble average[4] = {}; + gint n = 0; + gint x; + gint y; + gint c; + + for (y = rect->y; y < rect->y + rect->height; y++) + { + for (x = rect->x; x < rect->x + rect->width; x++) + { + gdouble sample[4]; + + if (gimp_pickable_get_pixel_at (pickable, + x, y, average_format, sample)) + { + for (c = 0; c < 4; c++) + average[c] += sample[c]; + + n++; + } + } + } + + if (n > 0) + { + for (c = 0; c < 4; c++) + average[c] /= n; + } + + babl_process (babl_fish (average_format, format), average, pixel, 1); +} + + +/* public functions */ + + void gimp_pickable_flush (GimpPickable *pickable) { @@ -182,6 +239,29 @@ gimp_pickable_get_pixel_at (GimpPickable *pickable, return FALSE; } +void +gimp_pickable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel) +{ + GimpPickableInterface *pickable_iface; + + g_return_if_fail (GIMP_IS_PICKABLE (pickable)); + g_return_if_fail (rect != NULL); + g_return_if_fail (pixel != NULL); + + if (! format) + format = gimp_pickable_get_format (pickable); + + pickable_iface = GIMP_PICKABLE_GET_INTERFACE (pickable); + + if (pickable_iface->get_pixel_average) + pickable_iface->get_pixel_average (pickable, rect, format, pixel); + else + memset (pixel, 0, babl_format_get_bytes_per_pixel (format)); +} + gboolean gimp_pickable_get_color_at (GimpPickable *pickable, gint x, @@ -300,45 +380,31 @@ gimp_pickable_pick_color (GimpPickable *pickable, gdouble sample[4]; g_return_val_if_fail (GIMP_IS_PICKABLE (pickable), FALSE); + g_return_val_if_fail (color != NULL, FALSE); format = gimp_pickable_get_format (pickable); if (! gimp_pickable_get_pixel_at (pickable, x, y, format, sample)) return FALSE; - gimp_pickable_pixel_to_srgb (pickable, format, sample, color); - if (pixel) memcpy (pixel, sample, babl_format_get_bytes_per_pixel (format)); if (sample_average) { - gint count = 0; - gdouble color_avg[4] = { 0.0, 0.0, 0.0, 0.0 }; - gint radius = (gint) average_radius; - gint i, j; + gint radius = floor (average_radius); format = babl_format ("RaGaBaA double"); - for (i = x - radius; i <= x + radius; i++) - for (j = y - radius; j <= y + radius; j++) - if (gimp_pickable_get_pixel_at (pickable, i, j, format, sample)) - { - count++; - - color_avg[RED] += sample[RED]; - color_avg[GREEN] += sample[GREEN]; - color_avg[BLUE] += sample[BLUE]; - color_avg[ALPHA] += sample[ALPHA]; - } - - sample[RED] = color_avg[RED] / count; - sample[GREEN] = color_avg[GREEN] / count; - sample[BLUE] = color_avg[BLUE] / count; - sample[ALPHA] = color_avg[ALPHA] / count; - - gimp_pickable_pixel_to_srgb (pickable, format, sample, color); + gimp_pickable_get_pixel_average (pickable, + GEGL_RECTANGLE (x - radius, + y - radius, + 2 * radius + 1, + 2 * radius + 1), + format, sample); } + gimp_pickable_pixel_to_srgb (pickable, format, sample, color); + return TRUE; } diff --git a/app/core/gimppickable.h b/app/core/gimppickable.h index 5e52aa5756..f59c223945 100644 --- a/app/core/gimppickable.h +++ b/app/core/gimppickable.h @@ -35,68 +35,76 @@ struct _GimpPickableInterface GTypeInterface base_iface; /* virtual functions */ - void (* flush) (GimpPickable *pickable); - GimpImage * (* get_image) (GimpPickable *pickable); - const Babl * (* get_format) (GimpPickable *pickable); - const Babl * (* get_format_with_alpha) (GimpPickable *pickable); - GeglBuffer * (* get_buffer) (GimpPickable *pickable); - gboolean (* get_pixel_at) (GimpPickable *pickable, - gint x, - gint y, - const Babl *format, - gpointer pixel); - gdouble (* get_opacity_at) (GimpPickable *pickable, - gint x, - gint y); - void (* pixel_to_srgb) (GimpPickable *pickable, - const Babl *format, - gpointer pixel, - GimpRGB *color); - void (* srgb_to_pixel) (GimpPickable *pickable, - const GimpRGB *color, - const Babl *format, - gpointer pixel); + void (* flush) (GimpPickable *pickable); + GimpImage * (* get_image) (GimpPickable *pickable); + const Babl * (* get_format) (GimpPickable *pickable); + const Babl * (* get_format_with_alpha) (GimpPickable *pickable); + GeglBuffer * (* get_buffer) (GimpPickable *pickable); + gboolean (* get_pixel_at) (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); + gdouble (* get_opacity_at) (GimpPickable *pickable, + gint x, + gint y); + void (* get_pixel_average) (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); + void (* pixel_to_srgb) (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); + void (* srgb_to_pixel) (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); }; GType gimp_pickable_interface_get_type (void) G_GNUC_CONST; -void gimp_pickable_flush (GimpPickable *pickable); -GimpImage * gimp_pickable_get_image (GimpPickable *pickable); -const Babl * gimp_pickable_get_format (GimpPickable *pickable); -const Babl * gimp_pickable_get_format_with_alpha (GimpPickable *pickable); -GeglBuffer * gimp_pickable_get_buffer (GimpPickable *pickable); -gboolean gimp_pickable_get_pixel_at (GimpPickable *pickable, - gint x, - gint y, - const Babl *format, - gpointer pixel); -gboolean gimp_pickable_get_color_at (GimpPickable *pickable, - gint x, - gint y, - GimpRGB *color); -gdouble gimp_pickable_get_opacity_at (GimpPickable *pickable, - gint x, - gint y); -void gimp_pickable_pixel_to_srgb (GimpPickable *pickable, - const Babl *format, - gpointer pixel, - GimpRGB *color); -void gimp_pickable_srgb_to_pixel (GimpPickable *pickable, - const GimpRGB *color, - const Babl *format, - gpointer pixel); -void gimp_pickable_srgb_to_image_color (GimpPickable *pickable, - const GimpRGB *color, - GimpRGB *image_color); +void gimp_pickable_flush (GimpPickable *pickable); +GimpImage * gimp_pickable_get_image (GimpPickable *pickable); +const Babl * gimp_pickable_get_format (GimpPickable *pickable); +const Babl * gimp_pickable_get_format_with_alpha (GimpPickable *pickable); +GeglBuffer * gimp_pickable_get_buffer (GimpPickable *pickable); +gboolean gimp_pickable_get_pixel_at (GimpPickable *pickable, + gint x, + gint y, + const Babl *format, + gpointer pixel); +gboolean gimp_pickable_get_color_at (GimpPickable *pickable, + gint x, + gint y, + GimpRGB *color); +gdouble gimp_pickable_get_opacity_at (GimpPickable *pickable, + gint x, + gint y); +void gimp_pickable_get_pixel_average (GimpPickable *pickable, + const GeglRectangle *rect, + const Babl *format, + gpointer pixel); +void gimp_pickable_pixel_to_srgb (GimpPickable *pickable, + const Babl *format, + gpointer pixel, + GimpRGB *color); +void gimp_pickable_srgb_to_pixel (GimpPickable *pickable, + const GimpRGB *color, + const Babl *format, + gpointer pixel); +void gimp_pickable_srgb_to_image_color (GimpPickable *pickable, + const GimpRGB *color, + GimpRGB *image_color); -gboolean gimp_pickable_pick_color (GimpPickable *pickable, - gint x, - gint y, - gboolean sample_average, - gdouble average_radius, - gpointer pixel, - GimpRGB *color); +gboolean gimp_pickable_pick_color (GimpPickable *pickable, + gint x, + gint y, + gboolean sample_average, + gdouble average_radius, + gpointer pixel, + GimpRGB *color); #endif /* __GIMP_PICKABLE_H__ */