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