2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
19 #include <histogrampanel.h>
20 #include <multilangmgr.h>
22 HistogramPanel::HistogramPanel () {
24 histogramArea
= Gtk::manage (new HistogramArea ());
25 showRed
= Gtk::manage (new Gtk::ToggleButton ("R"));
26 showGreen
= Gtk::manage (new Gtk::ToggleButton ("G"));
27 showBlue
= Gtk::manage (new Gtk::ToggleButton ("B"));
28 showValue
= Gtk::manage (new Gtk::ToggleButton ("L"));
29 Gtk::VBox
* vbox
= Gtk::manage (new Gtk::VBox (false, 0));
31 showRed
->set_active (true);
32 showGreen
->set_active (true);
33 showBlue
->set_active (true);
34 showValue
->set_active (true);
35 vbox
->pack_start (*showRed
, Gtk::PACK_SHRINK
, 2);
36 vbox
->pack_start (*showGreen
, Gtk::PACK_SHRINK
, 2);
37 vbox
->pack_start (*showBlue
, Gtk::PACK_SHRINK
, 2);
38 vbox
->pack_start (*showValue
, Gtk::PACK_SHRINK
, 2);
39 pack_start (*histogramArea
);
40 pack_end (*vbox
, Gtk::PACK_SHRINK
, 2);
42 showRed
->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled
) );
43 showGreen
->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled
) );
44 showBlue
->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled
) );
45 showValue
->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::rgbv_toggled
) );
49 showRed
->set_tooltip_text (M("HISTOGRAM_TOOLTIP_R"));
50 showGreen
->set_tooltip_text (M("HISTOGRAM_TOOLTIP_G"));
51 showBlue
->set_tooltip_text (M("HISTOGRAM_TOOLTIP_B"));
52 showValue
->set_tooltip_text (M("HISTOGRAM_TOOLTIP_L"));
54 rconn
= signal_size_allocate().connect( sigc::mem_fun(*this, &HistogramPanel::resized
) );
57 void HistogramPanel::resized (Gtk::Allocation
& req
) {
61 if (req
.get_width()/2>150)
62 set_size_request (req
.get_width(), 150);
64 set_size_request (req
.get_width(), req
.get_width()/2);
66 histogramArea
->renderHistogram ();
67 histogramArea
->queue_draw ();
70 void HistogramPanel::rgbv_toggled () {
72 histogramArea
->updateOptions (showRed
->get_active(), showGreen
->get_active(), showBlue
->get_active(), showValue
->get_active());
73 histogramArea
->queue_draw ();
76 HistogramArea::HistogramArea () :
77 needVal(true), needRed(true), needGreen(true), needBlue(true), oldwidth(-1), valid(false), showFull(true) {
79 haih
= new HistogramAreaIdleHelper
;
81 haih
->destroyed
= false;
84 signal_style_changed().connect( sigc::mem_fun(*this, &HistogramArea::styleChanged
) );
87 HistogramArea::~HistogramArea () {
90 haih
->destroyed
= true;
95 void HistogramArea::updateOptions (bool r
, bool g
, bool b
, bool v
) {
105 int histupdate (void* data
) {
107 gdk_threads_enter ();
109 HistogramAreaIdleHelper
* haih
= (HistogramAreaIdleHelper
*)data
;
111 if (haih
->destroyed
) {
112 if (haih
->pending
== 1)
116 gdk_threads_leave ();
120 haih
->harea
->renderHistogram ();
121 haih
->harea
->queue_draw ();
124 gdk_threads_leave ();
128 void HistogramArea::update (unsigned int* rh
, unsigned int* gh
, unsigned int* bh
, unsigned int* lh
) {
131 memcpy (lhist
, lh
, 256*sizeof(unsigned int));
132 memcpy (rhist
, rh
, 256*sizeof(unsigned int));
133 memcpy (ghist
, gh
, 256*sizeof(unsigned int));
134 memcpy (bhist
, bh
, 256*sizeof(unsigned int));
141 g_idle_add (histupdate
, haih
);
144 void HistogramArea::renderHistogram () {
149 Glib::RefPtr
<Gdk::Window
> window
= get_window();
150 int winx
, winy
, winw
, winh
, wind
;
151 window
->get_geometry(winx
, winy
, winw
, winh
, wind
);
153 backBuffer
= Gdk::Pixmap::create (window
, winw
, winh
, -1);
155 Glib::RefPtr
<Gdk::GC
> bgc
= Gdk::GC::create(backBuffer
);
157 bgc
->set_foreground (white
);
158 backBuffer
->draw_rectangle (bgc
, true, 0, 0, winw
, winh
);
162 // compute height of the full histogram (realheight) and
164 int fullhistheight
= 0;
165 for (int i
=0; i
<256; i
++) {
166 if (needVal
&& lhist
[i
]>fullhistheight
)
167 fullhistheight
= lhist
[i
];
168 if (needRed
&& rhist
[i
]>fullhistheight
)
169 fullhistheight
= rhist
[i
];
170 if (needGreen
&& ghist
[i
]>fullhistheight
)
171 fullhistheight
= ghist
[i
];
172 if (needBlue
&& bhist
[i
]>fullhistheight
)
173 fullhistheight
= bhist
[i
];
176 // compute two hights, one for the magnified view and one for the threshold
177 int realhistheight
= fullhistheight
;
183 for (int i
=0; i
<fullhistheight
; i
++) {
184 for (int j
=0; j
<256; j
++)
185 if ((needVal
&& lhist
[j
]>i
) || (needRed
&& rhist
[j
]>i
) || (needGreen
&& ghist
[j
]>i
) || (needBlue
&& bhist
[j
]>i
))
187 if (area1thres
==0 && (double)area
/ (256*(i
+1)) < 0.3)
189 if (area2thres
==0 && (double)area
/ (256*(i
+1)) < 0.3)
191 if (area1thres
&& area2thres
)
194 if (area1thres
>0 && area2thres
>0 && area1thres
<fullhistheight
)
195 realhistheight
= area2thres
;
198 if (realhistheight
<winh
-2)
199 realhistheight
= winh
-2;
201 Cairo::RefPtr
<Cairo::Context
> cr
= backBuffer
->create_cairo_context();
202 cr
->set_antialias (Cairo::ANTIALIAS_SUBPIXEL
);
203 cr
->set_line_width (1.0);
204 double stepSize
= (winw
-1) / 256.0;
206 cr
->move_to (0, winh
-1);
207 cr
->set_source_rgb (0.75, 0.75, 0.75);
208 for (int i
=0; i
<256; i
++) {
209 double val
= lhist
[i
] * (double)(winh
-2) / realhistheight
;
213 cr
->line_to (i
*stepSize
, winh
-1-val
);
216 cr
->line_to (winw
-1, winh
-1);
cr
->fill_preserve ();
218 cr
->set_source_rgb (0.5, 0.5, 0.5);
222 cr
->move_to (0, winh
-1);
223 cr
->set_source_rgb (1.0, 0.0, 0.0);
224 for (int i
=0; i
<256; i
++) {
225 double val
= rhist
[i
] * (double)(winh
-2) / realhistheight
;
229 cr
->line_to (i
*stepSize
, winh
-1-val
);
233 if (needGreen
) {
cr
->move_to (0, winh
-1);
234 cr
->set_source_rgb (0.0, 1.0, 0.0);
235 for (int i
=0; i
<256; i
++) {
236 double val
= ghist
[i
] * (double)(winh
-2) / realhistheight
;
240 cr
->line_to (i
*stepSize
, winh
-1-val
);
245 cr
->move_to (0, winh
-1);
246 cr
->set_source_rgb (0.0, 0.0, 1.0);
247 for (int i
=0; i
<256; i
++) {
248 int val
= bhist
[i
] * (double)(winh
-2) / realhistheight
;
252 cr
->line_to (i
*stepSize
, winh
-1-val
);
260 // scale histogram to width winw-1
262 int* vval = new int[winw-1];
263 int* vred = new int[winw-1];
264 int* vgreen = new int[winw-1];
265 int* vblue = new int[winw-1];
267 memset (vval, 0, sizeof(int)*(winw-1));
268 memset (vred, 0, sizeof(int)*(winw-1));
269 memset (vgreen, 0, sizeof(int)*(winw-1));
270 memset (vblue, 0, sizeof(int)*(winw-1));
273 double scale = 256.0 / (winw-2);
274 for (int i=0; i<=winw-2; i++) {
276 while (index < 256 && (int)(index/scale) == i) {
277 vval[i] += lhist[index];
278 vred[i] += rhist[index];
279 vgreen[i] += ghist[index];
280 vblue[i] += bhist[index];
287 vgreen[i] /= samples;
292 // compute height of the full histogram (realheight) and
294 int fullhistheight = 0;
295 for (int i=0; i<=winw-2; i++) {
296 if (needVal && vval[i]>fullhistheight)
297 fullhistheight = vval[i];
298 if (needRed && vred[i]>fullhistheight)
299 fullhistheight = vred[i];
300 if (needGreen && vgreen[i]>fullhistheight)
301 fullhistheight = vgreen[i];
302 if (needBlue && vblue[i]>fullhistheight)
303 fullhistheight = vblue[i];
306 // compute two hights, one for the magnified view and one for the threshold
308 int realhistheight = fullhistheight;
314 for (int i=0; i<fullhistheight; i++) {
315 for (int j=0; j<winw-1; j++)
316 if ((needVal && vval[j]>i) || (needRed && vred[j]>i) || (needGreen && vgreen[j]>i) || (needBlue && vblue[j]>i))
318 if (area1thres==0 && (double)area / ((winw-1)*(i+1)) < 0.3)
320 if (area2thres==0 && (double)area / ((winw-1)*(i+1)) < 0.3)
322 if (area1thres && area2thres)
325 if (area1thres>0 && area2thres>0 && area1thres<fullhistheight)
326 realhistheight = area2thres;
329 if (realhistheight<winh-2)
330 realhistheight = winh-2;
332 Cairo::RefPtr<Cairo::Context> cr = backBuffer->create_cairo_context();
333 cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
334 cr->set_line_width (1.0);
336 cr->move_to (0, winh-1);
337 cr->set_source_rgb (0.75, 0.75, 0.75);
338 for (int i=0; i<=winw-2; i++) {
339 int val = (int)(vval[i] * (double)(winh-2) / realhistheight);
343 cr->line_to (i+1, winh-1-val);
345 cr->fill_preserve ();
346 cr->set_source_rgb (0.5, 0.5, 0.5);
350 cr->move_to (0, winh-1);
351 cr->set_source_rgb (1.0, 0.0, 0.0);
352 for (int i=0; i<=winw-2; i++) {
353 int val = (int)(vred[i] * (double)(winh-2) / realhistheight);
357 cr->line_to (i+1, winh-1-val);
362 cr->move_to (0, winh-1);
363 cr->set_source_rgb (0.0, 1.0, 0.0);
364 for (int i=0; i<=winw-2; i++) {
365 int val = (int)(vgreen[i] * (double)(winh-2) / realhistheight);
369 cr->line_to (i+1, winh-1-val);
374 cr->move_to (0, winh-1);
375 cr->set_source_rgb (0.0, 0.0, 1.0);
376 for (int i=0; i<=winw-2; i++) {
377 int val = (int)(vblue[i] * (double)(winh-2) / realhistheight);
381 cr->line_to (i+1, winh-1-val);
392 bgc
->set_foreground (mgray
);
393 backBuffer
->draw_rectangle (bgc
, false, 0, 0, winw
-1, winh
-1);
395 bgc
->set_line_attributes (1, Gdk::LINE_ON_OFF_DASH
, Gdk::CAP_NOT_LAST
, Gdk::JOIN_MITER
);
397 backBuffer
->draw_line (bgc
, winw
/3, 0, winw
/3, winh
-1);
398 backBuffer
->draw_line (bgc
, 2*winw
/3, 0, 2*winw
/3, winh
-1);
399 backBuffer
->draw_line (bgc
, 0, winh
/3, winw
-1, winh
/3);
400 backBuffer
->draw_line (bgc
, 0, 2*winh
/3, winw
-1, 2*winh
/3);
402 bgc
->set_line_attributes (1, Gdk::LINE_SOLID
, Gdk::CAP_NOT_LAST
, Gdk::JOIN_MITER
);
408 void HistogramArea::on_realize () {
410 Gtk::DrawingArea::on_realize();
411 Glib::RefPtr
<Gdk::Window
> window
= get_window();
412 gc_
= Gdk::GC::create(window
);
413 add_events(Gdk::EXPOSURE_MASK
| Gdk::BUTTON_PRESS_MASK
);
414 Glib::RefPtr
<Gdk::Colormap
> colormap
= get_default_colormap();
416 black
= Gdk::Color ("black");
417 red
= Gdk::Color ("red");
418 green
= Gdk::Color ("green");
419 blue
= Gdk::Color ("blue");
420 lgray
= Gdk::Color ("gray75");
421 mgray
= Gdk::Color ("gray50");
422 dgray
= Gdk::Color ("gray25");
423 colormap
->alloc_color(black
);
424 colormap
->alloc_color(white
);
425 colormap
->alloc_color(red
);
426 colormap
->alloc_color(green
);
427 colormap
->alloc_color(blue
);
428 colormap
->alloc_color(lgray
);
429 colormap
->alloc_color(mgray
);
430 colormap
->alloc_color(dgray
);
434 void HistogramArea::styleChanged (const Glib::RefPtr
<Gtk::Style
>& style
) {
436 white
= get_style()->get_base(Gtk::STATE_NORMAL
);
440 bool HistogramArea::on_expose_event(GdkEventExpose
* event
) {
442 Glib::RefPtr
<Gdk::Window
> window
= get_window();
444 int winx
, winy
, winw
, winh
, wind
;
445 window
->get_geometry(winx
, winy
, winw
, winh
, wind
);
447 if (winw
!=oldwidth
&& winh
!=oldheight
)
449 window
->draw_drawable (gc_
, backBuffer
, 0, 0, 0, 0, -1, -1);
455 bool HistogramArea::on_button_press_event (GdkEventButton
* event
) {
457 if (event
->type
==GDK_2BUTTON_PRESS
&& event
->button
==1) {
458 showFull
= !showFull
;