1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 #include "config.h"
19 
20 #include <string.h>
21 
22 #include <gegl.h>
23 #include <gtk/gtk.h>
24 
25 #include "libgimpmath/gimpmath.h"
26 
27 #include "widgets-types.h"
28 
29 #include "core/gimphistogram.h"
30 #include "core/gimpmarshal.h"
31 
32 #include "gimphistogramview.h"
33 
34 
35 #define MIN_WIDTH  64
36 #define MIN_HEIGHT 64
37 
38 enum
39 {
40   RANGE_CHANGED,
41   LAST_SIGNAL
42 };
43 
44 enum
45 {
46   PROP_0,
47   PROP_CHANNEL,
48   PROP_SCALE,
49   PROP_BORDER_WIDTH,
50   PROP_SUBDIVISIONS
51 };
52 
53 
54 static void     gimp_histogram_view_dispose        (GObject        *object);
55 static void     gimp_histogram_view_set_property   (GObject        *object,
56                                                     guint           property_id,
57                                                     const GValue   *value,
58                                                     GParamSpec     *pspec);
59 static void     gimp_histogram_view_get_property   (GObject        *object,
60                                                     guint           property_id,
61                                                     GValue         *value,
62                                                     GParamSpec     *pspec);
63 
64 static void     gimp_histogram_view_size_request   (GtkWidget      *widget,
65                                                     GtkRequisition *requisition);
66 static gboolean gimp_histogram_view_expose         (GtkWidget      *widget,
67                                                     GdkEventExpose *event);
68 static gboolean gimp_histogram_view_button_press   (GtkWidget      *widget,
69                                                     GdkEventButton *bevent);
70 static gboolean gimp_histogram_view_button_release (GtkWidget      *widget,
71                                                     GdkEventButton *bevent);
72 static gboolean gimp_histogram_view_motion_notify  (GtkWidget      *widget,
73                                                     GdkEventMotion *bevent);
74 
75 static void     gimp_histogram_view_notify      (GimpHistogram        *histogram,
76                                                  const GParamSpec     *pspec,
77                                                  GimpHistogramView    *view);
78 static void     gimp_histogram_view_update_bins (GimpHistogramView    *view);
79 
80 static void     gimp_histogram_view_draw_spike  (GimpHistogramView    *view,
81                                                  GimpHistogramChannel  channel,
82                                                  cairo_t              *cr,
83                                                  const GdkColor       *fg_color,
84                                                  cairo_operator_t      fg_operator,
85                                                  const GdkColor       *bg_color,
86                                                  gint                  x,
87                                                  gint                  i,
88                                                  gint                  j,
89                                                  gdouble               max,
90                                                  gdouble               bg_max,
91                                                  gint                  height,
92                                                  gint                  border);
93 
94 
95 G_DEFINE_TYPE (GimpHistogramView, gimp_histogram_view,
96                GTK_TYPE_DRAWING_AREA)
97 
98 #define parent_class gimp_histogram_view_parent_class
99 
100 static guint histogram_view_signals[LAST_SIGNAL] = { 0 };
101 
102 
103 static void
gimp_histogram_view_class_init(GimpHistogramViewClass * klass)104 gimp_histogram_view_class_init (GimpHistogramViewClass *klass)
105 {
106   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
107   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
108 
109   histogram_view_signals[RANGE_CHANGED] =
110     g_signal_new ("range-changed",
111                   G_TYPE_FROM_CLASS (klass),
112                   G_SIGNAL_RUN_FIRST,
113                   G_STRUCT_OFFSET (GimpHistogramViewClass, range_changed),
114                   NULL, NULL,
115                   gimp_marshal_VOID__INT_INT,
116                   G_TYPE_NONE, 2,
117                   G_TYPE_INT,
118                   G_TYPE_INT);
119 
120   object_class->dispose              = gimp_histogram_view_dispose;
121   object_class->get_property         = gimp_histogram_view_get_property;
122   object_class->set_property         = gimp_histogram_view_set_property;
123 
124   widget_class->size_request         = gimp_histogram_view_size_request;
125   widget_class->expose_event         = gimp_histogram_view_expose;
126   widget_class->button_press_event   = gimp_histogram_view_button_press;
127   widget_class->button_release_event = gimp_histogram_view_button_release;
128   widget_class->motion_notify_event  = gimp_histogram_view_motion_notify;
129 
130   klass->range_changed               = NULL;
131 
132   g_object_class_install_property (object_class, PROP_CHANNEL,
133                                    g_param_spec_enum ("histogram-channel",
134                                                       NULL, NULL,
135                                                       GIMP_TYPE_HISTOGRAM_CHANNEL,
136                                                       GIMP_HISTOGRAM_VALUE,
137                                                       GIMP_PARAM_READWRITE |
138                                                       G_PARAM_CONSTRUCT));
139 
140   g_object_class_install_property (object_class, PROP_SCALE,
141                                    g_param_spec_enum ("histogram-scale",
142                                                       NULL, NULL,
143                                                       GIMP_TYPE_HISTOGRAM_SCALE,
144                                                       GIMP_HISTOGRAM_SCALE_LINEAR,
145                                                       GIMP_PARAM_READWRITE |
146                                                       G_PARAM_CONSTRUCT));
147 
148   g_object_class_install_property (object_class, PROP_BORDER_WIDTH,
149                                    g_param_spec_int ("border-width", NULL, NULL,
150                                                      0, 32, 1,
151                                                      GIMP_PARAM_READWRITE |
152                                                      G_PARAM_CONSTRUCT));
153 
154   g_object_class_install_property (object_class, PROP_SUBDIVISIONS,
155                                    g_param_spec_int ("subdivisions",
156                                                      NULL, NULL,
157                                                      1, 64, 5,
158                                                      GIMP_PARAM_READWRITE |
159                                                      G_PARAM_CONSTRUCT));
160 }
161 
162 static void
gimp_histogram_view_init(GimpHistogramView * view)163 gimp_histogram_view_init (GimpHistogramView *view)
164 {
165   view->histogram    = NULL;
166   view->bg_histogram = NULL;
167   view->n_bins       = 256;
168   view->start        = 0;
169   view->end          = 255;
170 }
171 
172 static void
gimp_histogram_view_dispose(GObject * object)173 gimp_histogram_view_dispose (GObject *object)
174 {
175   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (object);
176 
177   gimp_histogram_view_set_histogram (view, NULL);
178   gimp_histogram_view_set_background (view, NULL);
179 
180   G_OBJECT_CLASS (parent_class)->dispose (object);
181 }
182 
183 static void
gimp_histogram_view_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)184 gimp_histogram_view_set_property (GObject      *object,
185                                   guint         property_id,
186                                   const GValue *value,
187                                   GParamSpec   *pspec)
188 {
189   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (object);
190 
191   switch (property_id)
192     {
193     case PROP_CHANNEL:
194       view->channel = g_value_get_enum (value);
195       gtk_widget_queue_draw (GTK_WIDGET (view));
196       break;
197     case PROP_SCALE:
198       view->scale = g_value_get_enum (value);
199       gtk_widget_queue_draw (GTK_WIDGET (view));
200       break;
201     case PROP_BORDER_WIDTH:
202       view->border_width = g_value_get_int (value);
203       gtk_widget_queue_resize (GTK_WIDGET (view));
204       break;
205     case PROP_SUBDIVISIONS:
206       view->subdivisions = g_value_get_int (value);
207       gtk_widget_queue_draw (GTK_WIDGET (view));
208       break;
209 
210    default:
211       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
212       break;
213     }
214 }
215 
216 static void
gimp_histogram_view_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)217 gimp_histogram_view_get_property (GObject      *object,
218                                   guint         property_id,
219                                   GValue       *value,
220                                   GParamSpec   *pspec)
221 {
222   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (object);
223 
224   switch (property_id)
225     {
226     case PROP_CHANNEL:
227       g_value_set_enum (value, view->channel);
228       break;
229     case PROP_SCALE:
230       g_value_set_enum (value, view->scale);
231       break;
232     case PROP_BORDER_WIDTH:
233       g_value_set_int (value, view->border_width);
234       break;
235     case PROP_SUBDIVISIONS:
236       g_value_set_int (value, view->subdivisions);
237       break;
238 
239    default:
240       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
241       break;
242     }
243 }
244 
245 static void
gimp_histogram_view_size_request(GtkWidget * widget,GtkRequisition * requisition)246 gimp_histogram_view_size_request (GtkWidget      *widget,
247                                   GtkRequisition *requisition)
248 {
249   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (widget);
250 
251   requisition->width  = MIN_WIDTH  + 2 * view->border_width;
252   requisition->height = MIN_HEIGHT + 2 * view->border_width;
253 }
254 
255 static gdouble
gimp_histogram_view_get_maximum(GimpHistogramView * view,GimpHistogram * histogram,GimpHistogramChannel channel)256 gimp_histogram_view_get_maximum (GimpHistogramView    *view,
257                                  GimpHistogram        *histogram,
258                                  GimpHistogramChannel  channel)
259 {
260   gdouble max = gimp_histogram_get_maximum (histogram, channel);
261 
262   switch (view->scale)
263     {
264     case GIMP_HISTOGRAM_SCALE_LINEAR:
265       break;
266 
267     case GIMP_HISTOGRAM_SCALE_LOGARITHMIC:
268       if (max > 0.0)
269         max = log (max);
270       else
271         max = 1.0;
272       break;
273     }
274 
275   return max;
276 }
277 
278 static gboolean
gimp_histogram_view_expose(GtkWidget * widget,GdkEventExpose * event)279 gimp_histogram_view_expose (GtkWidget      *widget,
280                             GdkEventExpose *event)
281 {
282   GimpHistogramView *view  = GIMP_HISTOGRAM_VIEW (widget);
283   GtkStyle          *style = gtk_widget_get_style (widget);
284   GtkAllocation      allocation;
285   cairo_t           *cr;
286   gint               x;
287   gint               x1, x2;
288   gint               border;
289   gint               width, height;
290   gdouble            max    = 0.0;
291   gdouble            bg_max = 0.0;
292   gint               xstop;
293   GdkColor          *color_in;
294   GdkColor          *color_out;
295   GdkColor          *bg_color_in;
296   GdkColor          *bg_color_out;
297   GdkColor           rgb_color[3];
298 
299   cr = gdk_cairo_create (gtk_widget_get_window (widget));
300 
301   gdk_cairo_region (cr, event->region);
302   cairo_clip (cr);
303 
304   /*  Draw the background  */
305   gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_NORMAL]);
306   cairo_paint (cr);
307 
308   gtk_widget_get_allocation (widget, &allocation);
309 
310   border = view->border_width;
311   width  = allocation.width  - 2 * border;
312   height = allocation.height - 2 * border;
313 
314   cairo_set_line_width (cr, 1.0);
315   cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
316   cairo_translate (cr, 0.5, 0.5);
317 
318   /*  Draw the outer border  */
319   gdk_cairo_set_source_color (cr, &style->text_aa[GTK_STATE_NORMAL]);
320   cairo_rectangle (cr, border, border,
321                    width - 1, height - 1);
322   cairo_stroke (cr);
323 
324   if (! view->histogram && ! view->bg_histogram)
325     {
326       cairo_destroy (cr);
327       return FALSE;
328     }
329 
330   x1 = CLAMP (MIN (view->start, view->end), 0, view->n_bins - 1);
331   x2 = CLAMP (MAX (view->start, view->end), 0, view->n_bins - 1);
332 
333   if (view->histogram)
334     max = gimp_histogram_view_get_maximum (view, view->histogram,
335                                            view->channel);
336 
337   if (view->bg_histogram)
338     bg_max = gimp_histogram_view_get_maximum (view, view->bg_histogram,
339                                               view->channel);
340 
341   color_in  = &style->text[GTK_STATE_SELECTED];
342   color_out = &style->text[GTK_STATE_NORMAL];
343 
344   bg_color_in  = &style->mid[GTK_STATE_SELECTED];
345   bg_color_out = &style->mid[GTK_STATE_NORMAL];
346 
347   if (view->channel == GIMP_HISTOGRAM_RGB)
348     {
349       for (x = 0; x < 3; x++)
350         {
351           rgb_color[x].red   = (x == 0 ? 0xFFFF : 0x0);
352           rgb_color[x].green = (x == 1 ? 0xFFFF : 0x0);
353           rgb_color[x].blue  = (x == 2 ? 0xFFFF : 0x0);
354         }
355     }
356 
357   xstop = 1;
358   for (x = 0; x < width; x++)
359     {
360       gboolean  in_selection = FALSE;
361 
362       gint  i = (x * view->n_bins) / width;
363       gint  j = ((x + 1) * view->n_bins) / width;
364 
365       if (! (x1 == 0 && x2 == (view->n_bins - 1)))
366         {
367           gint k = i;
368 
369           do
370             in_selection |= (x1 <= k && k <= x2);
371           while (++k < j);
372         }
373 
374       if (view->subdivisions > 1 && x >= (xstop * width / view->subdivisions))
375         {
376           gdk_cairo_set_source_color (cr, &style->text_aa[GTK_STATE_NORMAL]);
377 
378           cairo_move_to (cr, x + border, border);
379           cairo_line_to (cr, x + border, border + height - 1);
380           cairo_stroke (cr);
381 
382           xstop++;
383         }
384       else if (in_selection)
385         {
386           gdk_cairo_set_source_color (cr, &style->base[GTK_STATE_SELECTED]);
387 
388           cairo_move_to (cr, x + border, border);
389           cairo_line_to (cr, x + border, border + height - 1);
390           cairo_stroke (cr);
391         }
392 
393       if (view->channel == GIMP_HISTOGRAM_RGB)
394         {
395           gint c;
396 
397           for (c = 0; c < 3; c++)
398             gimp_histogram_view_draw_spike (view, GIMP_HISTOGRAM_RED + c, cr,
399                                             &style->black,
400                                             CAIRO_OPERATOR_OVER,
401                                             NULL,
402                                             x, i, j, max, bg_max, height, border);
403 
404           for (c = 0; c < 3; c++)
405             gimp_histogram_view_draw_spike (view, GIMP_HISTOGRAM_RED + c, cr,
406                                             &rgb_color[c],
407                                             CAIRO_OPERATOR_ADD,
408                                             NULL,
409                                             x, i, j, max, bg_max, height, border);
410 
411           gimp_histogram_view_draw_spike (view, view->channel, cr,
412                                           in_selection ? color_in : color_out,
413                                           CAIRO_OPERATOR_OVER,
414                                           NULL,
415                                           x, i, j, max, bg_max, height, border);
416         }
417       else
418         {
419           gimp_histogram_view_draw_spike (view, view->channel, cr,
420                                           in_selection ? color_in : color_out,
421                                           CAIRO_OPERATOR_OVER,
422                                           in_selection ? bg_color_in : bg_color_out,
423                                           x, i, j, max, bg_max, height, border);
424         }
425     }
426 
427   cairo_destroy (cr);
428 
429   return FALSE;
430 }
431 
432 static void
gimp_histogram_view_draw_spike(GimpHistogramView * view,GimpHistogramChannel channel,cairo_t * cr,const GdkColor * fg_color,cairo_operator_t fg_operator,const GdkColor * bg_color,gint x,gint i,gint j,gdouble max,gdouble bg_max,gint height,gint border)433 gimp_histogram_view_draw_spike (GimpHistogramView    *view,
434                                 GimpHistogramChannel  channel,
435                                 cairo_t              *cr,
436                                 const GdkColor       *fg_color,
437                                 cairo_operator_t      fg_operator,
438                                 const GdkColor       *bg_color,
439                                 gint                  x,
440                                 gint                  i,
441                                 gint                  j,
442                                 gdouble               max,
443                                 gdouble               bg_max,
444                                 gint                  height,
445                                 gint                  border)
446 {
447   gdouble value    = 0.0;
448   gdouble bg_value = 0.0;
449   gint    y;
450   gint    bg_y;
451 
452   if (view->histogram)
453     {
454       gint ii = i;
455 
456       do
457         {
458           gdouble v = gimp_histogram_get_value (view->histogram,
459                                                 channel, ii++);
460 
461           if (v > value)
462             value = v;
463         }
464       while (ii < j);
465     }
466 
467   if (bg_color && view->bg_histogram)
468     {
469       gint ii = i;
470 
471       do
472         {
473           gdouble v = gimp_histogram_get_value (view->bg_histogram,
474                                                 channel, ii++);
475 
476           if (v > bg_value)
477             bg_value = v;
478         }
479       while (ii < j);
480     }
481 
482   if (value <= 0.0 && bg_value <= 0.0)
483     return;
484 
485   switch (view->scale)
486     {
487     case GIMP_HISTOGRAM_SCALE_LINEAR:
488       y    = (gint) (((height - 2) * value)    / max);
489       bg_y = (gint) (((height - 2) * bg_value) / bg_max);
490       break;
491 
492     case GIMP_HISTOGRAM_SCALE_LOGARITHMIC:
493       y    = (gint) (((height - 2) * log (value))    / max);
494       bg_y = (gint) (((height - 2) * log (bg_value)) / bg_max);
495       break;
496 
497     default:
498       y    = 0;
499       bg_y = 0;
500       break;
501     }
502 
503   if (bg_color)
504     {
505       gdk_cairo_set_source_color (cr, bg_color);
506 
507       cairo_move_to (cr, x + border, height + border - 1);
508       cairo_line_to (cr, x + border, height + border - bg_y - 1);
509 
510       cairo_stroke (cr);
511     }
512 
513   cairo_set_operator (cr, fg_operator);
514 
515   gdk_cairo_set_source_color (cr, fg_color);
516 
517   cairo_move_to (cr, x + border, height + border - 1);
518   cairo_line_to (cr, x + border, height + border - y - 1);
519 
520   cairo_stroke (cr);
521 
522   cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
523 }
524 
525 static gboolean
gimp_histogram_view_button_press(GtkWidget * widget,GdkEventButton * bevent)526 gimp_histogram_view_button_press (GtkWidget      *widget,
527                                   GdkEventButton *bevent)
528 {
529   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (widget);
530 
531   if (bevent->type == GDK_BUTTON_PRESS && bevent->button == 1)
532     {
533       GtkAllocation allocation;
534       gint          width;
535 
536       gtk_grab_add (widget);
537 
538       gtk_widget_get_allocation (widget, &allocation);
539 
540       width = allocation.width - 2 * view->border_width;
541 
542       view->start = CLAMP (((bevent->x - view->border_width) * view->n_bins) / width,
543                            0, view->n_bins - 1);
544       view->end   = view->start;
545 
546       gtk_widget_queue_draw (widget);
547     }
548 
549   return TRUE;
550 }
551 
552 static gboolean
gimp_histogram_view_button_release(GtkWidget * widget,GdkEventButton * bevent)553 gimp_histogram_view_button_release (GtkWidget      *widget,
554                                     GdkEventButton *bevent)
555 {
556   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (widget);
557 
558   if (bevent->button == 1)
559     {
560       gint start, end;
561 
562       gtk_grab_remove (widget);
563 
564       start = view->start;
565       end   = view->end;
566 
567       view->start = MIN (start, end);
568       view->end   = MAX (start, end);
569 
570       g_signal_emit (view, histogram_view_signals[RANGE_CHANGED], 0,
571                      view->start, view->end);
572     }
573 
574   return TRUE;
575 }
576 
577 static gboolean
gimp_histogram_view_motion_notify(GtkWidget * widget,GdkEventMotion * mevent)578 gimp_histogram_view_motion_notify (GtkWidget      *widget,
579                                    GdkEventMotion *mevent)
580 {
581   GimpHistogramView *view = GIMP_HISTOGRAM_VIEW (widget);
582   GtkAllocation      allocation;
583   gint               width;
584 
585   gtk_widget_get_allocation (widget, &allocation);
586 
587   width = allocation.width - 2 * view->border_width;
588 
589   view->start = CLAMP (((mevent->x - view->border_width) * view->n_bins) / width,
590                        0, view->n_bins - 1);
591 
592   gtk_widget_queue_draw (widget);
593 
594   return TRUE;
595 }
596 
597 
598 /*  public functions  */
599 
600 GtkWidget *
gimp_histogram_view_new(gboolean range)601 gimp_histogram_view_new (gboolean range)
602 {
603   GtkWidget *view = g_object_new (GIMP_TYPE_HISTOGRAM_VIEW, NULL);
604 
605   if (range)
606     gtk_widget_add_events (view,
607                            GDK_BUTTON_PRESS_MASK   |
608                            GDK_BUTTON_RELEASE_MASK |
609                            GDK_BUTTON1_MOTION_MASK);
610 
611   return view;
612 }
613 
614 void
gimp_histogram_view_set_histogram(GimpHistogramView * view,GimpHistogram * histogram)615 gimp_histogram_view_set_histogram (GimpHistogramView *view,
616                                    GimpHistogram     *histogram)
617 {
618   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
619 #if 0
620   g_return_if_fail (histogram == NULL ||
621                     view->bg_histogram == NULL ||
622                     gimp_histogram_n_components (view->bg_histogram) ==
623                     gimp_histogram_n_components (histogram));
624 #endif
625 
626   if (view->histogram != histogram)
627     {
628       if (view->histogram)
629         {
630           g_signal_handlers_disconnect_by_func (view->histogram,
631                                                 gimp_histogram_view_notify,
632                                                 view);
633           g_object_unref (view->histogram);
634         }
635 
636       view->histogram = histogram;
637 
638       if (histogram)
639         {
640           g_object_ref (histogram);
641 
642           g_signal_connect (histogram, "notify",
643                             G_CALLBACK (gimp_histogram_view_notify),
644                             view);
645 
646           if (! gimp_histogram_has_channel (histogram, view->channel))
647             gimp_histogram_view_set_channel (view, GIMP_HISTOGRAM_VALUE);
648         }
649 
650       gimp_histogram_view_update_bins (view);
651     }
652 
653   gtk_widget_queue_draw (GTK_WIDGET (view));
654 }
655 
656 GimpHistogram *
gimp_histogram_view_get_histogram(GimpHistogramView * view)657 gimp_histogram_view_get_histogram (GimpHistogramView *view)
658 {
659   g_return_val_if_fail (GIMP_IS_HISTOGRAM_VIEW (view), NULL);
660 
661   return view->histogram;
662 }
663 
664 void
gimp_histogram_view_set_background(GimpHistogramView * view,GimpHistogram * histogram)665 gimp_histogram_view_set_background (GimpHistogramView *view,
666                                     GimpHistogram     *histogram)
667 {
668   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
669 #if 0
670   g_return_if_fail (histogram == NULL ||
671                     view->histogram == NULL ||
672                     gimp_histogram_n_components (view->histogram) ==
673                     gimp_histogram_n_components (histogram));
674 #endif
675 
676   if (view->bg_histogram != histogram)
677     {
678       if (view->bg_histogram)
679         {
680           g_signal_handlers_disconnect_by_func (view->bg_histogram,
681                                                 gimp_histogram_view_notify,
682                                                 view);
683           g_object_unref (view->bg_histogram);
684         }
685 
686       view->bg_histogram = histogram;
687 
688       if (histogram)
689         {
690           g_object_ref (histogram);
691 
692           g_signal_connect (histogram, "notify",
693                             G_CALLBACK (gimp_histogram_view_notify),
694                             view);
695 
696           if (! gimp_histogram_has_channel (histogram, view->channel))
697             gimp_histogram_view_set_channel (view, GIMP_HISTOGRAM_VALUE);
698         }
699 
700       gimp_histogram_view_update_bins (view);
701     }
702 
703   gtk_widget_queue_draw (GTK_WIDGET (view));
704 }
705 
706 GimpHistogram *
gimp_histogram_view_get_background(GimpHistogramView * view)707 gimp_histogram_view_get_background (GimpHistogramView *view)
708 {
709   g_return_val_if_fail (GIMP_IS_HISTOGRAM_VIEW (view), NULL);
710 
711   return view->bg_histogram;
712 }
713 
714 void
gimp_histogram_view_set_channel(GimpHistogramView * view,GimpHistogramChannel channel)715 gimp_histogram_view_set_channel (GimpHistogramView    *view,
716                                  GimpHistogramChannel  channel)
717 {
718   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
719 
720   if (channel != view->channel)
721     g_object_set (view, "histogram-channel", channel, NULL);
722 }
723 
724 GimpHistogramChannel
gimp_histogram_view_get_channel(GimpHistogramView * view)725 gimp_histogram_view_get_channel (GimpHistogramView *view)
726 {
727   g_return_val_if_fail (GIMP_IS_HISTOGRAM_VIEW (view), 0);
728 
729   return view->channel;
730 }
731 
732 void
gimp_histogram_view_set_scale(GimpHistogramView * view,GimpHistogramScale scale)733 gimp_histogram_view_set_scale (GimpHistogramView  *view,
734                                GimpHistogramScale  scale)
735 {
736   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
737 
738   if (scale != view->scale)
739     g_object_set (view, "histogram-scale", scale, NULL);
740 }
741 
742 GimpHistogramScale
gimp_histogram_view_get_scale(GimpHistogramView * view)743 gimp_histogram_view_get_scale (GimpHistogramView *view)
744 {
745   g_return_val_if_fail (GIMP_IS_HISTOGRAM_VIEW (view), 0);
746 
747   return view->scale;
748 }
749 
750 void
gimp_histogram_view_set_range(GimpHistogramView * view,gint start,gint end)751 gimp_histogram_view_set_range (GimpHistogramView *view,
752                                gint               start,
753                                gint               end)
754 {
755   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
756 
757   if (view->start != MIN (start, end) ||
758       view->end   != MAX (start, end))
759     {
760       view->start = MIN (start, end);
761       view->end   = MAX (start, end);
762 
763       gtk_widget_queue_draw (GTK_WIDGET (view));
764 
765       g_signal_emit (view, histogram_view_signals[RANGE_CHANGED], 0,
766                      view->start, view->end);
767     }
768 }
769 
770 void
gimp_histogram_view_get_range(GimpHistogramView * view,gint * start,gint * end)771 gimp_histogram_view_get_range (GimpHistogramView *view,
772                                gint              *start,
773                                gint              *end)
774 {
775   g_return_if_fail (GIMP_IS_HISTOGRAM_VIEW (view));
776 
777   if (start) *start = view->start;
778   if (end)   *end   = view->end;
779 }
780 
781 
782 /*  private functions  */
783 
784 static void
gimp_histogram_view_notify(GimpHistogram * histogram,const GParamSpec * pspec,GimpHistogramView * view)785 gimp_histogram_view_notify (GimpHistogram     *histogram,
786                             const GParamSpec  *pspec,
787                             GimpHistogramView *view)
788 {
789   if (! strcmp (pspec->name, "n-bins"))
790     {
791       gimp_histogram_view_update_bins (view);
792     }
793   else
794     {
795       gtk_widget_queue_draw (GTK_WIDGET (view));
796     }
797 }
798 
799 static void
gimp_histogram_view_update_bins(GimpHistogramView * view)800 gimp_histogram_view_update_bins (GimpHistogramView *view)
801 {
802   gint new_bins = 0;
803 
804   if (view->histogram)
805     new_bins = gimp_histogram_n_bins (view->histogram);
806   else if (view->bg_histogram)
807     new_bins = gimp_histogram_n_bins (view->bg_histogram);
808 
809   if (new_bins > 0 && new_bins != view->n_bins)
810     {
811       view->start = MIN (ROUND ((gdouble) view->start *
812                                 new_bins / view->n_bins),
813                          new_bins - 1);
814       view->end   = MAX (ROUND ((gdouble) (view->end + 1) *
815                                 new_bins / view->n_bins) - 1,
816                          0);
817 
818       view->n_bins = new_bins;
819 
820       g_signal_emit (view, histogram_view_signals[RANGE_CHANGED], 0,
821                      view->start, view->end);
822     }
823 }
824