1 /*
2 * This file is part of RawTherapee.
3 *
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
5 *
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.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
18 */
19 #include "histogrampanel.h"
20 #include "multilangmgr.h"
21 #include "guiutils.h"
22 #include "options.h"
23 #include <cstring>
24 #include <cmath>
25 #include "../rtengine/array2D.h"
26 #include "../rtengine/LUT.h"
27 #include "rtimage.h"
28 #include "../rtengine/color.h"
29
30 using namespace rtengine;
31
32 constexpr float HistogramArea::MAX_BRIGHT;
33 constexpr float HistogramArea::MIN_BRIGHT;
34
35 using ScopeType = Options::ScopeType;
36
37 namespace {
38
39 constexpr float rgb_R[3] = { 1.f, 0.f, 0.f };
40 constexpr float rgb_G[3] = { 0.f, 1.f, 0.f };
41 constexpr float rgb_B[3] = { 0.f, 0.25f, 1.f };
42
set_arr(array2D<int> & dst,const array2D<int> & src)43 void set_arr(array2D<int> &dst, const array2D<int> &src)
44 {
45 dst(src.width(), src.height());
46 for (int y = 0; y < src.height(); ++y) {
47 for (int x = 0; x < src.width(); ++x) {
48 dst[y][x] = src[y][x];
49 }
50 }
51 }
52
53 constexpr double padding = 0;
54
55 } // namespace
56
57 //
58 //
59 // HistogramPanel
HistogramPanel()60 HistogramPanel::HistogramPanel () :
61 panel_listener(nullptr)
62 {
63 setExpandAlignProperties(this, true, true, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
64 set_name("HistogramPanel");
65
66 histogramArea = Gtk::manage (new HistogramArea (this));
67 setExpandAlignProperties(histogramArea, true, true, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
68
69 histogramRGBAreaHori.reset(new HistogramRGBAreaHori());
70 setExpandAlignProperties(histogramRGBAreaHori.get(), true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_END);
71
72 histogramRGBAreaVert.reset(new HistogramRGBAreaVert());
73 setExpandAlignProperties(histogramRGBAreaVert.get(), false, true, Gtk::ALIGN_END, Gtk::ALIGN_FILL);
74
75 switch (options.histogramScopeType) {
76 case ScopeType::NONE:
77 case ScopeType::HISTOGRAM_RAW:
78 case ScopeType::VECTORSCOPE_HC:
79 case ScopeType::VECTORSCOPE_HS:
80 histogramRGBArea = nullptr;
81 break;
82 case ScopeType::PARADE:
83 case ScopeType::WAVEFORM:
84 histogramRGBArea = histogramRGBAreaVert.get();
85 break;
86 case ScopeType::HISTOGRAM:
87 histogramRGBArea = histogramRGBAreaHori.get();
88 break;
89 }
90
91 // connecting the two childs
92 histogramArea->signal_factor_changed().connect( sigc::mem_fun(*histogramRGBAreaHori, &HistogramRGBArea::factorChanged) );
93 histogramArea->signal_factor_changed().connect( sigc::mem_fun(*histogramRGBAreaVert, &HistogramRGBArea::factorChanged) );
94
95 gfxGrid = Gtk::manage (new Gtk::Grid ());
96 gfxGrid->set_row_spacing(1);
97 gfxGrid->set_column_spacing(1);
98 gfxGrid->add(*histogramArea);
99 gfxGrid->attach_next_to(
100 *histogramRGBAreaVert, *histogramArea,
101 options.histogramPosition == 1 ? Gtk::POS_RIGHT : Gtk::POS_LEFT,
102 1,
103 1
104 );
105 gfxGrid->attach_next_to(*histogramRGBAreaHori, *histogramArea, Gtk::POS_BOTTOM, 1, 1);
106 histogramRGBAreaHori->set_no_show_all();
107 histogramRGBAreaVert->set_no_show_all();
108
109 redImage = new RTImage ("histogram-red-on-small.png");
110 greenImage = new RTImage ("histogram-green-on-small.png");
111 blueImage = new RTImage ("histogram-blue-on-small.png");
112 valueImage = new RTImage ("histogram-silver-on-small.png");
113 chroImage = new RTImage ("histogram-gold-on-small.png");
114 barImage = new RTImage ("histogram-bar-on-small.png");
115
116 redImage_g = new RTImage ("histogram-red-off-small.png");
117 greenImage_g = new RTImage ("histogram-green-off-small.png");
118 blueImage_g = new RTImage ("histogram-blue-off-small.png");
119 valueImage_g = new RTImage ("histogram-silver-off-small.png");
120 chroImage_g = new RTImage ("histogram-gold-off-small.png");
121 barImage_g = new RTImage ("histogram-bar-off-small.png");
122
123 mode_images_[0] = new RTImage ("histogram-mode-linear-small.png");
124 mode_images_[1] = new RTImage ("histogram-mode-logx-small.png");
125 mode_images_[2] = new RTImage ("histogram-mode-logxy-small.png");
126 mode_tips_[0] = M("HISTOGRAM_TOOLTIP_MODE_LINEAR");
127 mode_tips_[1] = M("HISTOGRAM_TOOLTIP_MODE_LOG_X");
128 mode_tips_[2] = M("HISTOGRAM_TOOLTIP_MODE_LOG_XY");
129
130 Gtk::Image* histImage = Gtk::manage(new RTImage("histogram-type-histogram-small.png"));
131 Gtk::Image* histRawImage = Gtk::manage(new RTImage("histogram-type-histogram-raw-small.png"));
132 Gtk::Image* paradeImage = Gtk::manage(new RTImage("histogram-type-parade-small.png"));
133 Gtk::Image* waveImage = Gtk::manage(new RTImage("histogram-type-waveform-small.png"));
134 Gtk::Image* vectHcImage = Gtk::manage(new RTImage("histogram-type-vectorscope-hc-small.png"));
135 Gtk::Image* vectHsImage = Gtk::manage(new RTImage("histogram-type-vectorscope-hs-small.png"));
136
137 showRed = Gtk::manage (new Gtk::ToggleButton ());
138 showGreen = Gtk::manage (new Gtk::ToggleButton ());
139 showBlue = Gtk::manage (new Gtk::ToggleButton ());
140 showValue = Gtk::manage (new Gtk::ToggleButton ());
141 showChro = Gtk::manage (new Gtk::ToggleButton ());
142 showMode = Gtk::manage (new Gtk::Button ());
143 showBAR = Gtk::manage (new Gtk::ToggleButton ());
144 scopeOptions = Gtk::manage (new Gtk::ToggleButton ());
145
146 Gtk::RadioButtonGroup scopeTypeGroup;
147 scopeHistBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
148 scopeHistRawBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
149 scopeParadeBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
150 scopeWaveBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
151 scopeVectHcBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
152 scopeVectHsBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
153 scopeHistBtn->set_mode(false);
154 scopeHistRawBtn->set_mode(false);
155 scopeParadeBtn->set_mode(false);
156 scopeWaveBtn->set_mode(false);
157 scopeVectHcBtn->set_mode(false);
158 scopeVectHsBtn->set_mode(false);
159
160 showRed->set_name("histButton");
161 showRed->set_can_focus(false);
162 showGreen->set_name("histButton");
163 showGreen->set_can_focus(false);
164 showBlue->set_name("histButton");
165 showBlue->set_can_focus(false);
166 showValue->set_name("histButton");
167 showValue->set_can_focus(false);
168 showChro->set_name("histButton");
169 showChro->set_can_focus(false);
170 showMode->set_name("histButton");
171 showMode->set_can_focus(false);
172 scopeOptions->set_name("histButton");
173 scopeOptions->set_can_focus(false);
174 showBAR->set_name("histButton");
175 showBAR->set_can_focus(false);
176 scopeHistBtn->set_name("histButton");
177 scopeHistBtn->set_can_focus(false);
178 scopeHistRawBtn->set_name("histButton");
179 scopeHistRawBtn->set_can_focus(false);
180 scopeParadeBtn->set_name("histButton");
181 scopeParadeBtn->set_can_focus(false);
182 scopeWaveBtn->set_name("histButton");
183 scopeWaveBtn->set_can_focus(false);
184 scopeVectHcBtn->set_name("histButton");
185 scopeVectHcBtn->set_can_focus(false);
186 scopeVectHsBtn->set_name("histButton");
187 scopeVectHsBtn->set_can_focus(false);
188
189 showRed->set_relief (Gtk::RELIEF_NONE);
190 showGreen->set_relief (Gtk::RELIEF_NONE);
191 showBlue->set_relief (Gtk::RELIEF_NONE);
192 showValue->set_relief (Gtk::RELIEF_NONE);
193 showChro->set_relief (Gtk::RELIEF_NONE);
194 showMode->set_relief (Gtk::RELIEF_NONE);
195 scopeOptions->set_relief (Gtk::RELIEF_NONE);
196 showBAR->set_relief (Gtk::RELIEF_NONE);
197 scopeHistBtn->set_relief (Gtk::RELIEF_NONE);
198 scopeHistRawBtn->set_relief (Gtk::RELIEF_NONE);
199 scopeParadeBtn->set_relief (Gtk::RELIEF_NONE);
200 scopeWaveBtn->set_relief (Gtk::RELIEF_NONE);
201 scopeVectHcBtn->set_relief (Gtk::RELIEF_NONE);
202 scopeVectHsBtn->set_relief (Gtk::RELIEF_NONE);
203
204 showRed->set_tooltip_text (M("HISTOGRAM_TOOLTIP_R"));
205 showGreen->set_tooltip_text (M("HISTOGRAM_TOOLTIP_G"));
206 showBlue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_B"));
207 showValue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_L"));
208 showChro->set_tooltip_text (M("HISTOGRAM_TOOLTIP_CHRO"));
209 showMode->set_tooltip_text (M("HISTOGRAM_TOOLTIP_MODE"));
210 scopeOptions->set_tooltip_text(M("HISTOGRAM_TOOLTIP_SHOW_OPTIONS"));
211 scopeHistBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM"));
212 scopeHistRawBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM_RAW"));
213 scopeParadeBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_PARADE"));
214 scopeWaveBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_WAVEFORM"));
215 scopeVectHcBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HC"));
216 scopeVectHsBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HS"));
217
218 buttonGrid = Gtk::manage (new Gtk::Grid ());
219 buttonGrid->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
220 persistentButtons = Gtk::manage(new Gtk::Box());
221 persistentButtons->set_orientation(Gtk::ORIENTATION_VERTICAL);
222 optionButtons = Gtk::manage(new Gtk::Box());
223 optionButtons->set_orientation(Gtk::ORIENTATION_VERTICAL);
224
225 showRed->set_active (options.histogramRed);
226 showGreen->set_active (options.histogramGreen);
227 showBlue->set_active (options.histogramBlue);
228 showValue->set_active (options.histogramLuma);
229 showChro->set_active (options.histogramChroma);
230 // no showMode->set_active(), as it's not a ToggleButton
231 scopeOptions->set_active(options.histogramShowOptionButtons);
232 showBAR->set_active (options.histogramBar);
233
234 showRed->set_image (showRed->get_active() ? *redImage : *redImage_g);
235 showGreen->set_image (showGreen->get_active() ? *greenImage : *greenImage_g);
236 showBlue->set_image (showBlue->get_active() ? *blueImage : *blueImage_g);
237 showValue->set_image (showValue->get_active() ? *valueImage : *valueImage_g);
238 showChro->set_image (showChro->get_active() ? *chroImage : *chroImage_g);
239 toggleButtonMode();
240 scopeHistBtn->set_image(*histImage);
241 scopeHistRawBtn->set_image(*histRawImage);
242 scopeParadeBtn->set_image(*paradeImage);
243 scopeWaveBtn->set_image(*waveImage);
244 scopeVectHcBtn->set_image(*vectHcImage);
245 scopeVectHsBtn->set_image(*vectHsImage);
246 switch(options.histogramScopeType) {
247 case ScopeType::HISTOGRAM:
248 scopeHistBtn->set_active();
249 break;
250 case ScopeType::HISTOGRAM_RAW:
251 scopeHistRawBtn->set_active();
252 break;
253 case ScopeType::PARADE:
254 scopeParadeBtn->set_active();
255 break;
256 case ScopeType::WAVEFORM:
257 scopeWaveBtn->set_active();
258 break;
259 case ScopeType::VECTORSCOPE_HS:
260 scopeVectHsBtn->set_active();
261 break;
262 case ScopeType::VECTORSCOPE_HC:
263 scopeVectHcBtn->set_active();
264 break;
265 case ScopeType::NONE:
266 break;
267 }
268 scopeOptions->set_image(*Gtk::manage(new RTImage("histogram-ellipsis-small.png")));
269 showBAR->set_image (showBAR->get_active() ? *barImage : *barImage_g);
270
271 setExpandAlignProperties(showRed , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
272 setExpandAlignProperties(showGreen, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
273 setExpandAlignProperties(showBlue , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
274 setExpandAlignProperties(showValue, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
275 setExpandAlignProperties(showChro , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
276 setExpandAlignProperties(showMode , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
277 setExpandAlignProperties(scopeOptions, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
278 setExpandAlignProperties(showBAR , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
279 setExpandAlignProperties(scopeOptions, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
280 setExpandAlignProperties(scopeHistBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
281 setExpandAlignProperties(scopeHistRawBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
282 setExpandAlignProperties(scopeParadeBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
283 setExpandAlignProperties(scopeWaveBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
284 setExpandAlignProperties(scopeVectHcBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
285 setExpandAlignProperties(scopeVectHsBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
286 setExpandAlignProperties(persistentButtons, false, true, Gtk::ALIGN_START, Gtk::ALIGN_FILL);
287 setExpandAlignProperties(optionButtons, false, true, Gtk::ALIGN_START, Gtk::ALIGN_FILL);
288
289 showRed->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::red_toggled), showRed );
290 showGreen->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::green_toggled), showGreen );
291 showBlue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::blue_toggled), showBlue );
292 showValue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::value_toggled), showValue );
293 showChro->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::chro_toggled), showChro );
294 showMode->signal_released().connect( sigc::mem_fun(*this, &HistogramPanel::mode_released), showMode );
295 scopeOptions->signal_toggled().connect(sigc::mem_fun(*this, &HistogramPanel::scopeOptionsToggled));
296 showBAR->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::bar_toggled), showBAR );
297 scopeHistBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeHistBtn));
298 scopeHistRawBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeHistRawBtn));
299 scopeParadeBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeParadeBtn));
300 scopeWaveBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeWaveBtn));
301 scopeVectHcBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeVectHcBtn));
302 scopeVectHsBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeVectHsBtn));
303
304 brightnessWidget = Gtk::manage(new Gtk::Scale(Gtk::ORIENTATION_VERTICAL));
305 brightnessWidget->set_inverted();
306 brightnessWidget->set_range(log(HistogramArea::MIN_BRIGHT), log(HistogramArea::MAX_BRIGHT));
307 brightnessWidget->set_draw_value(false);
308 brightnessWidget->signal_value_changed().connect(sigc::mem_fun(*this, &HistogramPanel::brightnessWidgetValueChanged));
309 brightnessWidget->set_name("histScale");
310 brightnessWidget->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TRACE_BRIGHTNESS"));
311 setExpandAlignProperties(brightnessWidget, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START);
312
313 optionButtons->add(*showRed);
314 optionButtons->add(*showGreen);
315 optionButtons->add(*showBlue);
316 optionButtons->add(*showValue);
317 optionButtons->add(*showChro);
318 optionButtons->add(*showMode);
319 optionButtons->add(*showBAR);
320 optionButtons->add(*brightnessWidget);
321
322 Gtk::VSeparator* separator = Gtk::manage(new Gtk::VSeparator());
323 setExpandAlignProperties(separator, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
324 persistentButtons->add(*scopeHistBtn);
325 persistentButtons->add(*scopeHistRawBtn);
326 persistentButtons->add(*scopeParadeBtn);
327 persistentButtons->add(*scopeWaveBtn);
328 persistentButtons->add(*scopeVectHsBtn);
329 persistentButtons->add(*scopeVectHcBtn);
330 persistentButtons->add(*separator);
331 persistentButtons->add(*scopeOptions);
332
333 // Put the button vbox next to the window's border to be less disturbing
334 if (options.histogramPosition == 1) {
335 buttonGrid->add(*persistentButtons);
336 buttonGrid->add(*optionButtons);
337
338 add (*buttonGrid);
339 add (*gfxGrid);
340 } else {
341 buttonGrid->add(*optionButtons);
342 buttonGrid->add(*persistentButtons);
343
344 add (*gfxGrid);
345 add (*buttonGrid);
346 }
347
348 show_all ();
349 optionButtons->set_no_show_all();
350 optionButtons->set_visible(options.histogramShowOptionButtons);
351
352 type_changed();
353 updateHistAreaOptions();
354 if (histogramRGBArea) {
355 updateHistRGBAreaOptions();
356 }
357
358 brightness_changed_connection = histogramArea->getBrighnessChangedSignal().connect(sigc::mem_fun(*this, &HistogramPanel::brightnessUpdated));
359 rconn = signal_size_allocate().connect( sigc::mem_fun(*this, &HistogramPanel::resized) );
360
361 histogramArea->setBrightness(options.histogramTraceBrightness);
362 }
363
~HistogramPanel()364 HistogramPanel::~HistogramPanel ()
365 {
366 delete redImage;
367 delete greenImage;
368 delete blueImage;
369 delete valueImage;
370 delete chroImage;
371 for (int i = 0; i < 3; ++i) {
372 delete mode_images_[i];
373 }
374 delete barImage;
375
376 delete redImage_g;
377 delete greenImage_g;
378 delete blueImage_g;
379 delete valueImage_g;
380 delete chroImage_g;
381 delete barImage_g;
382
383 }
384
showRGBBar()385 void HistogramPanel::showRGBBar()
386 {
387 histogramRGBAreaHori->set_visible(
388 histogramRGBArea == histogramRGBAreaHori.get() && showBAR->get_active());
389 histogramRGBAreaVert->set_visible(
390 histogramRGBArea == histogramRGBAreaVert.get() && showBAR->get_active());
391 histogramRGBAreaHori->setShow(false);
392 histogramRGBAreaVert->setShow(false);
393
394 if (!histogramRGBArea) {
395 return;
396 }
397
398 setHistRGBInvalid();
399 histogramRGBArea->setShow(showBAR->get_active());
400 }
401
resized(Gtk::Allocation & req)402 void HistogramPanel::resized (Gtk::Allocation& req)
403 {
404 static int old_height = 0;
405 static int old_width = 0;
406
407 bool size_changed =
408 old_height != req.get_height() || old_width != req.get_width();
409
410 if (!histogramArea->updatePending() && size_changed) {
411 histogramArea->updateBackBuffer ();
412 histogramArea->queue_draw ();
413 }
414
415 // set histogramRGBArea invalid;
416 if (histogramRGBArea && size_changed) {
417 histogramRGBArea->updateBackBuffer(-1, -1, -1);
418 histogramRGBArea->queue_draw ();
419 }
420
421 // Store current height of the histogram
422 options.histogramHeight = get_height();
423
424 old_height = req.get_height();
425 old_width = req.get_width();
426 }
427
red_toggled()428 void HistogramPanel::red_toggled ()
429 {
430 showRed->set_image(showRed->get_active() ? *redImage : *redImage_g);
431 rgbv_toggled();
432 }
green_toggled()433 void HistogramPanel::green_toggled ()
434 {
435 showGreen->set_image(showGreen->get_active() ? *greenImage : *greenImage_g);
436 rgbv_toggled();
437 }
blue_toggled()438 void HistogramPanel::blue_toggled ()
439 {
440 showBlue->set_image(showBlue->get_active() ? *blueImage : *blueImage_g);
441 rgbv_toggled();
442 }
value_toggled()443 void HistogramPanel::value_toggled ()
444 {
445 removeIfThere(showValue, valueImage, false);
446 removeIfThere(showValue, valueImage_g, false);
447 showValue->set_image(showValue->get_active() ? *valueImage : *valueImage_g);
448 rgbv_toggled();
449 }
chro_toggled()450 void HistogramPanel::chro_toggled ()
451 {
452 removeIfThere(showChro, chroImage, false);
453 removeIfThere(showChro, chroImage_g, false);
454 showChro->set_image(showChro->get_active() ? *chroImage : *chroImage_g);
455 rgbv_toggled();
456 }
457
mode_released()458 void HistogramPanel::mode_released ()
459 {
460 options.histogramDrawMode = (options.histogramDrawMode + 1) % 3;
461 toggleButtonMode();
462 rgbv_toggled();
463 }
464
brightnessWidgetValueChanged(void)465 void HistogramPanel::brightnessWidgetValueChanged(void)
466 {
467 ConnectionBlocker blocker(brightness_changed_connection);
468 histogramArea->setBrightness(exp(brightnessWidget->get_value()));
469 options.histogramTraceBrightness = histogramArea->getBrightness();
470 }
471
brightnessUpdated(float brightness)472 void HistogramPanel::brightnessUpdated(float brightness)
473 {
474 brightnessWidget->set_value(log(brightness));
475 options.histogramTraceBrightness = histogramArea->getBrightness();
476 }
477
scopeOptionsToggled()478 void HistogramPanel::scopeOptionsToggled()
479 {
480 options.histogramShowOptionButtons = scopeOptions->get_active();
481 optionButtons->set_visible(scopeOptions->get_active());
482 }
483
type_selected(Gtk::RadioButton * button)484 void HistogramPanel::type_selected(Gtk::RadioButton* button)
485 {
486 ScopeType new_type = ScopeType::NONE;
487
488 if (button == scopeHistBtn) {
489 new_type = ScopeType::HISTOGRAM;
490 } else if (button == scopeHistRawBtn) {
491 new_type = ScopeType::HISTOGRAM_RAW;
492 } else if (button == scopeParadeBtn) {
493 new_type = ScopeType::PARADE;
494 } else if (button == scopeWaveBtn) {
495 new_type = ScopeType::WAVEFORM;
496 } else if (button == scopeVectHcBtn) {
497 new_type = ScopeType::VECTORSCOPE_HC;
498 } else if (button == scopeVectHsBtn) {
499 new_type = ScopeType::VECTORSCOPE_HS;
500 }
501
502 if (new_type == options.histogramScopeType) {
503 return;
504 }
505
506 options.histogramScopeType = new_type;
507
508 type_changed();
509 updateHistAreaOptions();
510 if (histogramRGBArea) {
511 updateHistRGBAreaOptions();
512 }
513 histogramArea->setDirty(true);
514 histogramArea->queue_draw();
515 }
516
type_changed()517 void HistogramPanel::type_changed()
518 {
519 switch (options.histogramScopeType) {
520 case ScopeType::HISTOGRAM:
521 showRed->show();
522 showGreen->show();
523 showBlue->show();
524 showValue->show();
525 showChro->show();
526 showMode->show();
527 showBAR->show();
528 showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_BAR"));
529 brightnessWidget->hide();
530 histogramRGBArea = histogramRGBAreaHori.get();
531 break;
532 case ScopeType::HISTOGRAM_RAW:
533 showRed->show();
534 showGreen->show();
535 showBlue->show();
536 showValue->hide();
537 showChro->hide();
538 showMode->show();
539 showBAR->hide();
540 brightnessWidget->hide();
541 histogramRGBArea = nullptr;
542 break;
543 case ScopeType::PARADE:
544 case ScopeType::WAVEFORM:
545 showRed->show();
546 showGreen->show();
547 showBlue->show();
548 showValue->show();
549 showChro->hide();
550 showMode->hide();
551 showBAR->show();
552 showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_BAR"));
553 brightnessWidget->show();
554 histogramRGBArea = histogramRGBAreaVert.get();
555 break;
556 case ScopeType::VECTORSCOPE_HC:
557 case ScopeType::VECTORSCOPE_HS:
558 showRed->hide();
559 showGreen->hide();
560 showBlue->hide();
561 showValue->hide();
562 showChro->hide();
563 showMode->hide();
564 showBAR->show();
565 showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_CROSSHAIR"));
566 brightnessWidget->show();
567 histogramRGBArea = nullptr;
568 break;
569 case ScopeType::NONE:
570 break;
571 }
572
573 if (panel_listener) {
574 updateHistAreaOptions();
575 panel_listener->scopeTypeChanged(options.histogramScopeType);
576 }
577
578 showRGBBar();
579 }
580
bar_toggled()581 void HistogramPanel::bar_toggled ()
582 {
583 showBAR->set_image(showBAR->get_active() ? *barImage : *barImage_g);
584 rgbv_toggled();
585 showRGBBar();
586 }
587
rgbv_toggled()588 void HistogramPanel::rgbv_toggled ()
589 {
590 // Update Display
591 updateHistAreaOptions();
592 histogramArea->updateBackBuffer ();
593 histogramArea->queue_draw ();
594
595 if (histogramRGBArea) {
596 updateHistRGBAreaOptions();
597 histogramRGBArea->updateBackBuffer(-1, -1, -1);
598 histogramRGBArea->queue_draw ();
599 }
600 }
601
setHistRGBInvalid()602 void HistogramPanel::setHistRGBInvalid ()
603 {
604 // do something to un-show vertical bars
605 histogramRGBArea->updateBackBuffer(-1, -1, -1);
606 histogramRGBArea->queue_draw ();
607 }
608
pointerMoved(bool validPos,const Glib::ustring & profile,const Glib::ustring & profileW,int x,int y,int r,int g,int b,bool isRaw)609 void HistogramPanel::pointerMoved (bool validPos, const Glib::ustring &profile, const Glib::ustring &profileW, int x, int y, int r, int g, int b, bool isRaw)
610 {
611 bool update_hist_area = false;
612
613 if (!validPos) {
614 // do something to un-show vertical bars
615 if (histogramRGBArea) {
616 histogramRGBArea->updateBackBuffer(-1, -1, -1);
617 }
618 update_hist_area = histogramArea->updatePointer(-1, -1, -1);
619 } else {
620 // do something to show vertical bars
621 if (histogramRGBArea) {
622 histogramRGBArea->updateBackBuffer(r, g, b, profile, profileW);
623 }
624 update_hist_area = histogramArea->updatePointer(r, g, b, profile, profileW);
625 }
626 if (histogramRGBArea) {
627 histogramRGBArea->queue_draw();
628 }
629 if (update_hist_area) {
630 histogramArea->queue_draw();
631 }
632 }
633
634 /*
635 * Move the vertical button bar to the right side
636 * only allowed values for align are Gtk::POS_LEFT and Gtk::POS_RIGHT
637 */
reorder(Gtk::PositionType align)638 void HistogramPanel::reorder (Gtk::PositionType align)
639 {
640 if (align == Gtk::POS_LEFT) {
641 gfxGrid->reference();
642 removeIfThere(this, gfxGrid, false);
643 add (*gfxGrid);
644 gfxGrid->unreference();
645
646 gfxGrid->remove(*histogramRGBAreaVert);
647 gfxGrid->add(*histogramRGBAreaVert);
648
649 optionButtons->reference();
650 removeIfThere(buttonGrid, optionButtons, false);
651 buttonGrid->add(*optionButtons);
652 optionButtons->unreference();
653 } else {
654 buttonGrid->reference();
655 removeIfThere(this, buttonGrid, false);
656 add (*buttonGrid);
657 buttonGrid->unreference();
658
659 gfxGrid->remove(*histogramRGBAreaVert);
660 gfxGrid->attach_next_to(*histogramRGBAreaVert, *histogramArea, Gtk::POS_LEFT, 1, 1);
661
662 persistentButtons->reference();
663 removeIfThere(buttonGrid, persistentButtons, false);
664 buttonGrid->add(*persistentButtons);
665 persistentButtons->unreference();
666 }
667 }
668
669 // DrawModeListener interface:
toggleButtonMode()670 void HistogramPanel::toggleButtonMode ()
671 {
672 int m = LIM(options.histogramDrawMode, 0, 2);
673 showMode->set_image(*mode_images_[m]);
674 showMode->set_tooltip_text(Glib::ustring::compose(M("HISTOGRAM_TOOLTIP_MODE"), mode_tips_[m]));
675 }
676
setPanelListener(HistogramPanelListener * listener)677 void HistogramPanel::setPanelListener(HistogramPanelListener* listener)
678 {
679 panel_listener = listener;
680
681 if (listener) {
682 listener->scopeTypeChanged(options.histogramScopeType);
683 }
684 }
685
updateHistAreaOptions()686 void HistogramPanel::updateHistAreaOptions()
687 {
688 histogramArea->updateOptions(
689 showRed->get_active(),
690 showGreen->get_active(),
691 showBlue->get_active(),
692 showValue->get_active(),
693 showChro->get_active(),
694 options.histogramDrawMode,
695 options.histogramScopeType,
696 showBAR->get_active()
697 );
698 }
699
updateHistRGBAreaOptions()700 void HistogramPanel::updateHistRGBAreaOptions()
701 {
702 histogramRGBArea->updateOptions(
703 showRed->get_active(),
704 showGreen->get_active(),
705 showBlue->get_active(),
706 showValue->get_active(),
707 showChro->get_active(),
708 showBAR->get_active()
709 );
710 }
711
712 //
713 //
714 //
715 // HistogramScaling
716
HistogramScaling()717 HistogramScaling::HistogramScaling():
718 factor(10.0)
719 {
720 factor = options.histogram_scaling_factor;
721 }
722
723
log(double vsize,double val)724 double HistogramScaling::log(double vsize, double val)
725 {
726 //double factor = 10.0; // can be tuned if necessary - higher is flatter curve
727 return vsize * std::log(factor / (factor + val)) / std::log(factor / (factor + vsize));
728 }
729
730 //
731 //
732 //
733 // HistogramRGBArea
HistogramRGBArea()734 HistogramRGBArea::HistogramRGBArea () :
735 val(0), r(0), g(0), b(0), valid(false),
736 needRed(options.histogramRed), needGreen(options.histogramGreen), needBlue(options.histogramBlue),
737 needLuma(options.histogramLuma), needChroma(options.histogramChroma),
738 showMode(options.histogramBar), barDisplayed(options.histogramBar), parent(nullptr)
739 {
740 get_style_context()->add_class("drawingarea");
741 set_name("HistogramRGBArea");
742
743 harih = new HistogramRGBAreaIdleHelper;
744 harih->harea = this;
745 harih->destroyed = false;
746 harih->pending = 0;
747 }
748
~HistogramRGBArea()749 HistogramRGBArea::~HistogramRGBArea ()
750 {
751 idle_register.destroy();
752
753 if (harih->pending) {
754 harih->destroyed = true;
755 } else {
756 delete harih;
757 }
758 }
759
760
getPreferredThickness(int & min_thickness,int & natural_thickness) const761 void HistogramRGBArea::getPreferredThickness(int& min_thickness, int& natural_thickness) const
762 {
763 int minimumLength = 0;
764 int naturalLength = 0;
765 getPreferredLength(minimumLength, naturalLength);
766 getPreferredThicknessForLength(minimumLength, min_thickness, natural_thickness);
767 }
768
getPreferredLength(int & min_length,int & natural_length) const769 void HistogramRGBArea::getPreferredLength(int& min_length, int& natural_length) const
770 {
771 int s = RTScalable::getScale();
772 min_length = 60 * s;
773 natural_length = 200 * s;
774 }
775
getPreferredThicknessForLength(int length,int & min_thickness,int & natural_thickness) const776 void HistogramRGBArea::getPreferredThicknessForLength(int length, int& min_thickness, int& natural_thickness) const
777 {
778 int bThickness = length / 30;
779
780 int s = RTScalable::getScale();
781
782 if (bThickness > (10 * s)) {
783 bThickness = 10 * s;
784 } else if (bThickness < (5 * s)) {
785 bThickness = 5 * s;
786 }
787
788 min_thickness = bThickness;
789 natural_thickness = bThickness;
790 }
791
792 // unused?
getPreferredLengthForThickness(int thickness,int & min_length,int & natural_length) const793 void HistogramRGBArea::getPreferredLengthForThickness(int thickness, int& min_length, int& natural_length) const
794 {
795 getPreferredLength(min_length, natural_length);
796 }
797
getShow()798 bool HistogramRGBArea::getShow()
799 {
800 return(showMode);
801 }
802
setShow(bool show)803 void HistogramRGBArea::setShow(bool show)
804 {
805 showMode = show;
806 }
807
updateBackBuffer(int r,int g,int b,const Glib::ustring & profile,const Glib::ustring & profileW)808 void HistogramRGBArea::updateBackBuffer (int r, int g, int b, const Glib::ustring &profile, const Glib::ustring &profileW)
809 {
810 if (!get_realized () || !showMode || !(
811 options.histogramScopeType == ScopeType::HISTOGRAM
812 || options.histogramScopeType == ScopeType::PARADE
813 || options.histogramScopeType == ScopeType::WAVEFORM
814 )) {
815 return;
816 }
817
818 // Mostly not necessary, but should be in some case
819 GThreadLock lock; // All GUI access from idle_add callbacks or separate thread HAVE to be protected
820
821 Glib::RefPtr<Gdk::Window> window = get_window();
822 int winx, winy, winw, winh;
823 window->get_geometry(winx, winy, winw, winh);
824
825 double s = RTScalable::getScale();
826
827 // This will create or update the size of the BackBuffer::surface
828 setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, winw, winh, true);
829
830 if (surface) {
831 Cairo::RefPtr<Cairo::Context> cc = Cairo::Context::create(surface);
832
833 cc->set_source_rgba (0., 0., 0., 0.);
834 cc->set_operator (Cairo::OPERATOR_CLEAR);
835 cc->paint ();
836 cc->set_operator (Cairo::OPERATOR_OVER);
837
838 cc->set_antialias(Cairo::ANTIALIAS_NONE);
839 cc->set_line_width (1.0 * s);
840
841 if ( r != -1 && g != -1 && b != -1 ) {
842 if (needRed) {
843 // Red
844 cc->set_source_rgb(rgb_R[0], rgb_R[1], rgb_R[2]);
845 drawBar(cc, r, 255.0, winw, winh, s);
846 }
847
848 if (needGreen) {
849 // Green
850 cc->set_source_rgb(rgb_G[0], rgb_G[1], rgb_G[2]);
851 drawBar(cc, g, 255.0, winw, winh, s);
852 }
853
854 if (needBlue) {
855 // Blue
856 cc->set_source_rgb(rgb_B[0], rgb_B[1], rgb_B[2]);
857 drawBar(cc, b, 255.0, winw, winh, s);
858 }
859
860 if(
861 (needLuma || needChroma)
862 && (options.histogramScopeType == ScopeType::HISTOGRAM
863 || options.histogramScopeType == ScopeType::PARADE
864 || options.histogramScopeType == ScopeType::WAVEFORM)
865 ) {
866 float Lab_L, Lab_a, Lab_b;
867 rtengine::Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, Lab_L, Lab_a, Lab_b, options.rtSettings.HistogramWorking);
868
869 if (needLuma) {
870 // Luma
871 cc->set_source_rgb(1.0, 1.0, 1.0);
872 drawBar(cc, Lab_L, 100.0, winw, winh, s);
873 }
874
875 if (needChroma && options.histogramScopeType == ScopeType::HISTOGRAM) {
876 // Chroma
877 double chromaval = sqrt(Lab_a * Lab_a + Lab_b * Lab_b) / 1.8;
878 cc->set_source_rgb(0.9, 0.9, 0.0);
879 drawBar(cc, chromaval, 100.0, winw, winh, s);
880 }
881 }
882 }
883 }
884
885 setDirty(false);
886 }
887
update(int valh,int rh,int gh,int bh)888 void HistogramRGBArea::update (int valh, int rh, int gh, int bh)
889 {
890
891 if (valh) {
892 val = valh;
893 r = rh;
894 g = gh;
895 b = bh;
896 valid = true;
897 } else {
898 valid = false;
899 }
900
901 harih->pending++;
902
903 idle_register.add(
904 [this]() -> bool
905 {
906 if (harih->destroyed) {
907 if (harih->pending == 1) {
908 delete harih;
909 } else {
910 --harih->pending;
911 }
912
913 return false;
914 }
915
916 harih->harea->updateBackBuffer(-1, -1, -1);
917 harih->harea->queue_draw ();
918
919 --harih->pending;
920
921 return false;
922 }
923 );
924 }
925
updateOptions(bool r,bool g,bool b,bool l,bool c,bool bar)926 void HistogramRGBArea::updateOptions (bool r, bool g, bool b, bool l, bool c, bool bar)
927 {
928
929 options.histogramRed = needRed = r;
930 options.histogramGreen = needGreen = g;
931 options.histogramBlue = needBlue = b;
932 options.histogramLuma = needLuma = l;
933 options.histogramChroma = needChroma = c;
934 options.histogramBar = showMode = bar;
935
936 }
937
on_realize()938 void HistogramRGBArea::on_realize ()
939 {
940
941 Gtk::DrawingArea::on_realize();
942 add_events(Gdk::BUTTON_PRESS_MASK);
943 }
944
on_draw(const::Cairo::RefPtr<Cairo::Context> & cr)945 bool HistogramRGBArea::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
946 {
947
948 const Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
949 style->render_background(cr, 0, 0, get_width(), get_height());
950
951 // on_realize & updateBackBuffer have to be called before
952 if (surface) {
953 if (isDirty()) { // not sure this could happen...
954 updateBackBuffer(-1, -1, -1);
955 }
956
957 copySurface(cr, NULL);
958 }
959
960 style->render_frame (cr, 0, 0, get_width(), get_height());
961
962 return true;
963 }
964
on_button_press_event(GdkEventButton * event)965 bool HistogramRGBArea::on_button_press_event (GdkEventButton* event)
966 {
967
968 if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
969 // do something?
970 }
971
972 return true;
973 }
974
factorChanged(double newFactor)975 void HistogramRGBArea::factorChanged (double newFactor)
976 {
977 factor = newFactor;
978 options.histogram_scaling_factor = factor;
979 }
980
drawBar(Cairo::RefPtr<Cairo::Context> cc,double value,double max_value,int winw,int winh,double scale)981 void HistogramRGBAreaHori::drawBar(Cairo::RefPtr<Cairo::Context> cc, double value, double max_value, int winw, int winh, double scale)
982 {
983 double pos;
984 if (options.histogramDrawMode < 2) {
985 pos = padding + value * (winw - padding * 2.0) / max_value + 0.5 * scale;
986 } else {
987 pos = padding + HistogramScaling::log (max_value, value) * (winw - padding * 2.0) / max_value + 0.5 * scale;
988 }
989 cc->move_to(pos, 0.0);
990 cc->line_to(pos, winh - 0.0);
991 cc->stroke();
992 }
993
get_request_mode_vfunc() const994 Gtk::SizeRequestMode HistogramRGBAreaHori::get_request_mode_vfunc () const
995 {
996 return Gtk::SIZE_REQUEST_HEIGHT_FOR_WIDTH;
997 }
998
get_preferred_height_vfunc(int & minimum_height,int & natural_height) const999 void HistogramRGBAreaHori::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
1000 {
1001 getPreferredThickness(minimum_height, natural_height);
1002 }
1003
get_preferred_width_vfunc(int & minimum_width,int & natural_width) const1004 void HistogramRGBAreaHori::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
1005 {
1006 getPreferredLength(minimum_width, natural_width);
1007 }
1008
get_preferred_height_for_width_vfunc(int width,int & minimum_height,int & natural_height) const1009 void HistogramRGBAreaHori::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
1010 {
1011 getPreferredThicknessForLength(width, minimum_height, natural_height);
1012 }
1013
get_preferred_width_for_height_vfunc(int height,int & minimum_width,int & natural_width) const1014 void HistogramRGBAreaHori::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
1015 {
1016 getPreferredLengthForThickness(height, minimum_width, natural_width);
1017 }
1018
drawBar(Cairo::RefPtr<Cairo::Context> cc,double value,double max_value,int winw,int winh,double scale)1019 void HistogramRGBAreaVert::drawBar(Cairo::RefPtr<Cairo::Context> cc, double value, double max_value, int winw, int winh, double scale)
1020 {
1021 double pos;
1022 if (options.histogramDrawMode < 2 || options.histogramScopeType == ScopeType::PARADE || options.histogramScopeType == ScopeType::WAVEFORM) {
1023 pos = padding + value * (winh - padding * 2.0 - 1) / max_value + 0.5 * scale;
1024 } else {
1025 pos = padding + HistogramScaling::log (max_value, value) * (winh - padding * 2.0) / max_value + 0.5 * scale;
1026 }
1027 cc->move_to(0.0, winh - pos);
1028 cc->line_to(winw, winh - pos);
1029 cc->stroke();
1030 }
1031
get_request_mode_vfunc() const1032 Gtk::SizeRequestMode HistogramRGBAreaVert::get_request_mode_vfunc () const
1033 {
1034 return Gtk::SIZE_REQUEST_WIDTH_FOR_HEIGHT;
1035 }
1036
get_preferred_height_vfunc(int & minimum_height,int & natural_height) const1037 void HistogramRGBAreaVert::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
1038 {
1039 getPreferredLength(minimum_height, natural_height);
1040 }
1041
get_preferred_width_vfunc(int & minimum_width,int & natural_width) const1042 void HistogramRGBAreaVert::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
1043 {
1044 minimum_width = 10 * RTScalable::getScale();
1045 natural_width = minimum_width;
1046 }
1047
get_preferred_height_for_width_vfunc(int width,int & minimum_height,int & natural_height) const1048 void HistogramRGBAreaVert::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
1049 {
1050 getPreferredLengthForThickness(width, minimum_height, natural_height);
1051 }
1052
get_preferred_width_for_height_vfunc(int height,int & minimum_width,int & natural_width) const1053 void HistogramRGBAreaVert::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
1054 {
1055 get_preferred_width_vfunc(minimum_width, natural_width);
1056 }
1057
1058 //
1059 //
1060 //
1061 // HistogramArea
HistogramArea(DrawModeListener * fml,bool is_main)1062 HistogramArea::HistogramArea(DrawModeListener *fml, bool is_main):
1063 vectorscope_scale(0),
1064 vect_hc(0, 0), vect_hs(0, 0),
1065 vect_hc_buffer_dirty(true), vect_hs_buffer_dirty(true),
1066 waveform_scale(0),
1067 rwave(0, 0), gwave(0, 0), bwave(0, 0), lwave(0, 0),
1068 parade_buffer_r_dirty(true), parade_buffer_g_dirty(true), parade_buffer_b_dirty(true),
1069 wave_buffer_dirty(true), wave_buffer_luma_dirty(true),
1070 valid(false), drawMode(options.histogramDrawMode), myDrawModeListener(fml),
1071 scopeType(options.histogramScopeType),
1072 oldwidth(-1), oldheight(-1),
1073 trace_brightness(1.0),
1074 needRed(options.histogramRed), needGreen(options.histogramGreen), needBlue(options.histogramBlue),
1075 needLuma(options.histogramLuma), needChroma(options.histogramChroma),
1076 isPressed(false), movingPosition(0.0),
1077 needPointer(options.histogramBar),
1078 pointer_red(-1), pointer_green(-1), pointer_blue(-1),
1079 pointer_a(0), pointer_b(0),
1080 is_main_(is_main)
1081 {
1082
1083 rhist(256);
1084 ghist(256);
1085 bhist(256);
1086 lhist(256);
1087 chist(256);
1088
1089 get_style_context()->add_class("drawingarea");
1090 set_name("HistogramArea");
1091
1092 haih = new HistogramAreaIdleHelper;
1093 haih->harea = this;
1094 haih->destroyed = false;
1095 haih->pending = 0;
1096 }
1097
~HistogramArea()1098 HistogramArea::~HistogramArea ()
1099 {
1100 idle_register.destroy();
1101
1102 if (haih->pending) {
1103 haih->destroyed = true;
1104 } else {
1105 delete haih;
1106 }
1107 }
1108
get_request_mode_vfunc() const1109 Gtk::SizeRequestMode HistogramArea::get_request_mode_vfunc () const
1110 {
1111 return Gtk::SIZE_REQUEST_CONSTANT_SIZE;
1112 }
1113
get_preferred_height_vfunc(int & minimum_height,int & natural_height) const1114 void HistogramArea::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
1115 {
1116 int s = RTScalable::getScale();
1117 minimum_height = 100 * s;
1118 natural_height = 200 * s;
1119 }
1120
get_preferred_width_vfunc(int & minimum_width,int & natural_width) const1121 void HistogramArea::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
1122 {
1123
1124 int s = RTScalable::getScale();
1125 minimum_width = 200 * s;
1126 natural_width = 400 * s;
1127 }
1128
get_preferred_height_for_width_vfunc(int width,int & minimum_height,int & natural_height) const1129 void HistogramArea::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
1130 {
1131
1132 minimum_height = 0;
1133 natural_height = 0;
1134 }
1135
get_preferred_width_for_height_vfunc(int height,int & minimum_width,int & natural_width) const1136 void HistogramArea::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
1137 {
1138 get_preferred_width_vfunc (minimum_width, natural_width);
1139 }
1140
updateOptions(bool r,bool g,bool b,bool l,bool c,int mode,ScopeType type,bool pointer)1141 void HistogramArea::updateOptions (bool r, bool g, bool b, bool l, bool c, int mode, ScopeType type, bool pointer)
1142 {
1143 wave_buffer_dirty = wave_buffer_dirty || needRed != r || needGreen != g || needBlue != b;
1144
1145 needRed = r;
1146 needGreen = g;
1147 needBlue = b;
1148 needLuma = l;
1149 needChroma = c;
1150 drawMode = mode;
1151 scopeType = type;
1152 needPointer = pointer;
1153
1154 if (is_main_) {
1155 options.histogramRed = needRed;
1156 options.histogramGreen = needGreen;
1157 options.histogramBlue = needBlue;
1158 options.histogramLuma = needLuma;
1159 options.histogramChroma = needChroma;
1160 options.histogramDrawMode = drawMode;
1161 options.histogramScopeType = scopeType;
1162 options.histogramBar = needPointer;
1163 }
1164 }
1165
updatePending(void)1166 bool HistogramArea::updatePending(void)
1167 {
1168 return haih->pending > 0 && !haih->destroyed;
1169 }
1170
update(const LUTu & histRed,const LUTu & histGreen,const LUTu & histBlue,const LUTu & histLuma,const LUTu & histChroma,const LUTu & histRedRaw,const LUTu & histGreenRaw,const LUTu & histBlueRaw,int vectorscopeScale,const array2D<int> & vectorscopeHC,const array2D<int> & vectorscopeHS,int waveformScale,const array2D<int> & waveformRed,const array2D<int> & waveformGreen,const array2D<int> & waveformBlue,const array2D<int> & waveformLuma)1171 void HistogramArea::update(
1172 const LUTu& histRed,
1173 const LUTu& histGreen,
1174 const LUTu& histBlue,
1175 const LUTu& histLuma,
1176 const LUTu& histChroma,
1177 const LUTu& histRedRaw,
1178 const LUTu& histGreenRaw,
1179 const LUTu& histBlueRaw,
1180 int vectorscopeScale,
1181 const array2D<int>& vectorscopeHC,
1182 const array2D<int>& vectorscopeHS,
1183 int waveformScale,
1184 const array2D<int>& waveformRed,
1185 const array2D<int>& waveformGreen,
1186 const array2D<int>& waveformBlue,
1187 const array2D<int>& waveformLuma
1188 )
1189 {
1190 if (histRed) {
1191 switch (scopeType) {
1192 case ScopeType::HISTOGRAM:
1193 rhist = histRed;
1194 ghist = histGreen;
1195 bhist = histBlue;
1196 lhist = histLuma;
1197 chist = histChroma;
1198 break;
1199 case ScopeType::HISTOGRAM_RAW:
1200 rhistRaw = histRedRaw;
1201 ghistRaw = histGreenRaw;
1202 bhistRaw = histBlueRaw;
1203 break;
1204 case ScopeType::PARADE:
1205 case ScopeType::WAVEFORM:
1206 waveform_scale = waveformScale;
1207 set_arr(rwave, waveformRed);
1208 set_arr(gwave, waveformGreen);
1209 set_arr(bwave, waveformBlue);
1210 set_arr(lwave, waveformLuma);
1211 parade_buffer_r_dirty = parade_buffer_g_dirty = parade_buffer_b_dirty = wave_buffer_dirty = wave_buffer_luma_dirty = true;
1212 break;
1213 case ScopeType::VECTORSCOPE_HS:
1214 vectorscope_scale = vectorscopeScale;
1215 set_arr(vect_hs, vectorscopeHS);
1216 vect_hs_buffer_dirty = true;
1217 break;
1218 case ScopeType::VECTORSCOPE_HC:
1219 vectorscope_scale = vectorscopeScale;
1220 set_arr(vect_hc, vectorscopeHC);
1221 vect_hc_buffer_dirty = true;
1222 break;
1223 case ScopeType::NONE:
1224 break;
1225 }
1226 valid = true;
1227 } else {
1228 valid = false;
1229 }
1230
1231 haih->pending++;
1232
1233 // Can be done outside of the GUI thread
1234 idle_register.add(
1235 [this]() -> bool
1236 {
1237 if (haih->destroyed) {
1238 if (haih->pending == 1) {
1239 delete haih;
1240 } else {
1241 --haih->pending;
1242 }
1243
1244 return false;
1245 }
1246
1247 haih->harea->setDirty(true);
1248 haih->harea->updateBackBuffer();
1249 haih->harea->queue_draw();
1250
1251 --haih->pending;
1252
1253 return false;
1254 }
1255 );
1256 }
1257
updateBackBuffer(int custom_w,int custom_h)1258 void HistogramArea::updateBackBuffer(int custom_w, int custom_h)
1259 {
1260 int winw = custom_w;
1261 int winh = custom_h;
1262 if (winw < 0) {
1263 if (!get_realized ()) {
1264 return;
1265 }
1266
1267 Glib::RefPtr<Gdk::Window> window = get_window();
1268 int winx, winy;
1269 window->get_geometry(winx, winy, winw, winh);
1270 }
1271
1272 // This will create or update the size of the BackBuffer::surface
1273 setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, winw, winh, true);
1274
1275 Cairo::RefPtr<Cairo::Context> cr = Cairo::Context::create(surface);
1276 const Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
1277
1278 double s = RTScalable::getScale();
1279
1280 // Setup drawing
1281 cr->set_source_rgba (0., 0., 0., 0.);
1282 cr->set_operator (Cairo::OPERATOR_CLEAR);
1283 cr->paint ();
1284
1285 cr->set_operator (Cairo::OPERATOR_SOURCE);
1286
1287 // Prepare drawing gridlines first
1288 cr->set_source_rgba (1., 1., 1., 0.25);
1289 cr->set_line_width (1.0 * s);
1290 cr->set_antialias(Cairo::ANTIALIAS_NONE);
1291 cr->set_line_join(Cairo::LINE_JOIN_MITER);
1292 cr->set_line_cap(Cairo::LINE_CAP_BUTT);
1293 std::valarray<double> ch_ds (1);
1294 ch_ds[0] = 4;
1295 cr->set_dash (ch_ds, 0);
1296
1297 bool rawMode = (scopeType == ScopeType::HISTOGRAM_RAW);
1298 if (rawMode) {
1299 updateRaw(cr);
1300 } else {
1301 updateNonRaw(cr);
1302 }
1303
1304 // Draw the frame's border
1305 style->render_frame(cr, 0, 0, surface->get_width(), surface->get_height());
1306
1307 oldwidth = w;
1308 oldheight = h;
1309
1310 setDirty(false);
1311 }
1312
1313
updateNonRaw(Cairo::RefPtr<Cairo::Context> cr)1314 void HistogramArea::updateNonRaw(Cairo::RefPtr<Cairo::Context> cr)
1315 {
1316 set_has_tooltip(false);
1317
1318 const Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
1319
1320 double s = RTScalable::getScale();
1321
1322 // determine the number of h-gridlines based on current h
1323 int nrOfHGridPartitions = (int)rtengine::min (16.0, pow (2.0, floor ((h - 100) / 250) + 2));
1324 int nrOfVGridPartitions = 8; // always show 8 stops (lines at 1,3,7,15,31,63,127)
1325
1326 // draw vertical gridlines
1327 if (scopeType == ScopeType::HISTOGRAM) {
1328 if (drawMode == 0) {
1329 for (int i = 1; i < nrOfVGridPartitions; i++) {
1330 cr->move_to((pow(2.0,i) - 1) / 255.0 * w + 0.5, 0.);
1331 cr->line_to((pow(2.0,i) - 1) / 255.0 * w + 0.5, h);
1332 cr->stroke();
1333 }
1334 } else {
1335 for (int i = 1; i < nrOfVGridPartitions; i++) {
1336 cr->move_to(HistogramScaling::log(255, pow(2.0,i) - 1) / 255.0 * w + 0.5, 0.);
1337 cr->line_to(HistogramScaling::log(255, pow(2.0,i) - 1) / 255.0 * w + 0.5, h);
1338 cr->stroke();
1339 }
1340 }
1341 }
1342
1343 // draw horizontal gridlines
1344 if (scopeType == ScopeType::PARADE || scopeType == ScopeType::WAVEFORM) {
1345 for (int i = 0; i <= nrOfVGridPartitions; i++) {
1346 const double ypos = h - padding - (pow(2.0,i) - 1) * (h - 2 * padding - 1) / 255.0;
1347 cr->move_to(0, ypos);
1348 cr->line_to(w, ypos);
1349 cr->stroke();
1350 }
1351 } else if (scopeType == ScopeType::VECTORSCOPE_HC || scopeType == ScopeType::VECTORSCOPE_HS) {
1352 // Vectorscope has no gridlines.
1353 } else if (drawMode != 2) {
1354 for (int i = 1; i < nrOfHGridPartitions; i++) {
1355 cr->move_to (0., i * (double)h / nrOfHGridPartitions + 0.5);
1356 cr->line_to (w, i * (double)h / nrOfHGridPartitions + 0.5);
1357 cr->stroke ();
1358 }
1359 } else {
1360 for (int i = 1; i < nrOfHGridPartitions; i++) {
1361 cr->move_to (0., h - HistogramScaling::log (h, i * (double)h / nrOfHGridPartitions) + 0.5*s);
1362 cr->line_to (w, h - HistogramScaling::log (h, i * (double)h / nrOfHGridPartitions) + 0.5*s);
1363 cr->stroke ();
1364 }
1365 }
1366
1367 cr->unset_dash();
1368
1369 if (valid && scopeType == ScopeType::HISTOGRAM) {
1370 // For RAW mode use the other hists
1371 LUTu& rh = rhist;
1372 LUTu& gh = ghist;
1373 LUTu& bh = bhist;
1374
1375 // make copies of LUT for faster access
1376 unsigned int lhisttemp[256] ALIGNED16 {0}, chisttemp[256] ALIGNED16 {0}, rhtemp[256] ALIGNED16 {0}, ghtemp[256] ALIGNED16 {0}, bhtemp[256] ALIGNED16 {0};
1377 const int scale = 1;
1378
1379 for(int i = 0; i < 256; i++) {
1380 if(needLuma) {
1381 lhisttemp[i] = lhist[i];
1382 }
1383
1384 if(needChroma) {
1385 chisttemp[i] = chist[i];
1386 }
1387
1388 if(needRed) {
1389 rhtemp[i] = rh[i];
1390 }
1391
1392 if(needGreen) {
1393 ghtemp[i] = gh[i];
1394 }
1395
1396 if(needBlue) {
1397 bhtemp[i] = bh[i];
1398 }
1399 }
1400
1401 // Compute the highest point of the histogram for scaling
1402 // Values at far left and right end (0 and 255) are handled differently
1403
1404 unsigned int histheight = 0;
1405
1406 for (int i = 1; i < 255; i++) {
1407 if (needLuma && lhisttemp[i] > histheight) {
1408 histheight = lhisttemp[i];
1409 }
1410
1411 if (needChroma && chisttemp[i] > histheight) {
1412 histheight = chisttemp[i];
1413 }
1414
1415 if (needRed && rhtemp[i] > histheight) {
1416 histheight = rhtemp[i];
1417 }
1418
1419 if (needGreen && ghtemp[i] > histheight) {
1420 histheight = ghtemp[i];
1421 }
1422
1423 if (needBlue && bhtemp[i] > histheight) {
1424 histheight = bhtemp[i];
1425 }
1426 }
1427
1428 int realhistheight = histheight;
1429
1430 if (realhistheight < h - 2) {
1431 realhistheight = h - 2;
1432 }
1433
1434 cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
1435 cr->set_line_width (1.0 * s);
1436 cr->set_operator (Cairo::OPERATOR_OVER);
1437
1438 int ui = 0, oi = 0;
1439
1440 const bool rawMode = (scopeType == ScopeType::HISTOGRAM_RAW);
1441 if (needLuma && !rawMode) {
1442 drawCurve(cr, lhist, realhistheight, w, h);
1443 cr->set_source_rgba (0.65, 0.65, 0.65, 0.65);
1444 cr->fill ();
1445 drawMarks(cr, lhist, scale, w, ui, oi);
1446 }
1447
1448 if (needChroma && !rawMode) {
1449 drawCurve(cr, chist, realhistheight, w, h);
1450 cr->set_source_rgb (0.9, 0.9, 0.);
1451 cr->stroke ();
1452 drawMarks(cr, chist, scale, w, ui, oi);
1453 }
1454
1455 if (needRed) {
1456 drawCurve(cr, rh, realhistheight, w, h);
1457 cr->set_source_rgb(rgb_R[0], rgb_R[1], rgb_R[2]);
1458 cr->stroke ();
1459 drawMarks(cr, rh, scale, w, ui, oi);
1460 }
1461
1462 if (needGreen) {
1463 drawCurve(cr, gh, realhistheight, w, h);
1464 cr->set_source_rgb(rgb_G[0], rgb_G[1], rgb_G[2]);
1465 cr->stroke ();
1466 drawMarks(cr, gh, scale, w, ui, oi);
1467 }
1468
1469 if (needBlue) {
1470 drawCurve(cr, bh, realhistheight, w, h);
1471 cr->set_source_rgb(rgb_B[0], rgb_B[1], rgb_B[2]);
1472 cr->stroke ();
1473 drawMarks(cr, bh, scale, w, ui, oi);
1474 }
1475
1476 } else if (scopeType == ScopeType::PARADE && rwave.width() > 0) {
1477 drawParade(cr, w, h);
1478 } else if (scopeType == ScopeType::WAVEFORM && rwave.width() > 0) {
1479 drawWaveform(cr, w, h);
1480 } else if (scopeType == ScopeType::VECTORSCOPE_HC || scopeType == ScopeType::VECTORSCOPE_HS) {
1481 drawVectorscope(cr, w, h);
1482 }
1483 }
1484
1485
1486 namespace {
1487
1488 class RawIdxHelper {
1489 public:
RawIdxHelper(bool logscale,unsigned int ub,unsigned int width)1490 RawIdxHelper(bool logscale, unsigned int ub, unsigned int width):
1491 logscale_(logscale),
1492 ub_(ub)
1493 {
1494 incr_ = std::max(ub_ / width, 1u);
1495 }
1496
operator ()(unsigned int & cur,unsigned int & i) const1497 void operator()(unsigned int &cur, unsigned int &i) const
1498 {
1499 unsigned int j = i;
1500 i = cur;
1501 unsigned int next = cur;
1502 if (logscale_) {
1503 next = std::max(static_cast<unsigned int>(j * 1.3f), cur + 1u);
1504 } else {
1505 next = cur + incr_;
1506 }
1507 if (ub_ - next < next - cur) {
1508 next = ub_ + 1;
1509 }
1510 if (cur < ub_ && next > ub_) {
1511 cur = ub_;
1512 } else {
1513 cur = std::min(next, ub_+1);
1514 }
1515 }
1516
1517 private:
1518 bool logscale_;
1519 unsigned int ub_;
1520 unsigned int incr_;
1521 };
1522
1523 } // namespace
1524
updatePointer(int r,int g,int b,const Glib::ustring & profile,const Glib::ustring & profileW)1525 bool HistogramArea::updatePointer(int r, int g, int b, const Glib::ustring &profile, const Glib::ustring &profileW)
1526 {
1527 if (!needPointer || !(scopeType == ScopeType::VECTORSCOPE_HC || scopeType == ScopeType::VECTORSCOPE_HS)) {
1528 return false;
1529 }
1530 if (pointer_red == r && pointer_green == g && pointer_blue == b) {
1531 return false;
1532 }
1533
1534 float L;
1535 pointer_red = r;
1536 pointer_green = g;
1537 pointer_blue = b;
1538 Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, L, pointer_a, pointer_b, options.rtSettings.HistogramWorking);
1539 updateBackBuffer();
1540 return true;
1541 }
1542
updateRaw(Cairo::RefPtr<Cairo::Context> cr)1543 void HistogramArea::updateRaw(Cairo::RefPtr<Cairo::Context> cr)
1544 {
1545 const Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
1546 double s = RTScalable::getScale();
1547
1548 // determine the number of h-gridlines based on current h
1549 int nrOfHGridPartitions = (int)rtengine::min (16.0, pow (2.0, floor ((h - 100) / 250) + 2));
1550 double sz = 2;
1551 if (valid) {
1552 sz = rtengine::max(rhistRaw.getUpperBound(), ghistRaw.getUpperBound(), bhistRaw.getUpperBound());
1553 }
1554
1555 int minval[3];
1556 int mincount[3];
1557 int maxval[3];
1558 int maxcount[3];
1559 int peakval[3];
1560 int peakcount[3];
1561 int totcount[3];
1562 int totsum[3];
1563 const char *ch[3] = {"#FF0000", "#00FF00", "#0000FF"};
1564
1565 // draw vertical gridlines
1566 const double logmax = std::log2(sz);
1567 const bool logscale = drawMode > 0;
1568
1569 for (double i = sz / 2.0; ; i /= 2.0) {
1570 double x = i / sz;
1571 if (logscale) {
1572 x = logmax + std::log2(x);
1573 if (x <= 0) {
1574 break;
1575 }
1576 x /= logmax;
1577 }
1578 x *= w;
1579 cr->move_to(x, 0.);
1580 cr->line_to(x, h);
1581 cr->stroke();
1582 if (i <= 1.0) {
1583 break;
1584 }
1585 }
1586
1587 // draw horizontal gridlines
1588 if (drawMode == 2) {
1589 for (int i = 1; i < nrOfHGridPartitions; i++) {
1590 double y = double(i) / nrOfHGridPartitions;
1591 y = rtengine::log2lin(y, 10.0);
1592 cr->move_to(0., y * h);
1593 cr->line_to(w, y * h);
1594 cr->stroke();
1595 }
1596 } else {
1597 for (int i = 1; i < nrOfHGridPartitions; i++) {
1598 cr->move_to(0., i * (double)h / nrOfHGridPartitions + 0.5);
1599 cr->line_to(w, i * (double)h / nrOfHGridPartitions + 0.5);
1600 cr->stroke();
1601 }
1602 }
1603
1604 cr->unset_dash();
1605
1606 if (valid) {
1607 // For RAW mode use the other hists
1608 LUTu &rh = rhistRaw;
1609 LUTu &gh = ghistRaw;
1610 LUTu &bh = bhistRaw;
1611
1612 auto ub = rtengine::max(rh.getUpperBound(), gh.getUpperBound(), bh.getUpperBound());
1613
1614 const auto bin =
1615 [](LUTu &data, int i, int next) -> double
1616 {
1617 double val = 0.0;
1618 for (int j = i; j < next; ++j) {
1619 val += data[j];
1620 }
1621 val /= (next - i);
1622 return val;
1623 };
1624
1625 //const int delta = std::max(int(ub / std::max(w / 2, 1)), 1);
1626 //const float delta = 1.05f;
1627 RawIdxHelper next_raw_idx(logscale, ub, w);
1628 unsigned int next = 1;
1629 unsigned int histheight = 0;
1630
1631 const unsigned int off = drawMode == 2 ? 0 : 1;
1632 for (unsigned int i = off; i <= ub-off;) {
1633 if (i < rh.getSize()) {
1634 double val = bin(rh, i, next);
1635 if (histheight < val) {
1636 histheight = val;
1637 }
1638 }
1639 if (i < gh.getSize()) {
1640 double val = bin(gh, i, next);
1641 if (histheight < val) {
1642 histheight = val;
1643 }
1644 }
1645 if (i < bh.getSize()) {
1646 double val = bin(bh, i, next);
1647 if (histheight < val) {
1648 histheight = val;
1649 }
1650 }
1651
1652 next_raw_idx(next, i);
1653 }
1654
1655 int realhistheight = histheight;
1656
1657 if (realhistheight < h - 2) {
1658 realhistheight = h - 2;
1659 }
1660
1661 cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
1662 cr->set_line_width (1.0 * s);
1663 cr->set_operator (Cairo::OPERATOR_OVER);
1664
1665 int ui = 0, oi = 0;
1666
1667 if (needRed) {
1668 drawRawCurve(cr, rh, ub, realhistheight, w, h);
1669 cr->set_source_rgb(rgb_R[0], rgb_R[1], rgb_R[2]);
1670 cr->stroke();
1671 drawMarks(cr, rh, 1.0, w, ui, oi);
1672 }
1673
1674 if (needGreen) {
1675 drawRawCurve(cr, gh, ub, realhistheight, w, h);
1676 cr->set_source_rgb(rgb_G[0], rgb_G[1], rgb_G[2]);
1677 cr->stroke();
1678 drawMarks(cr, gh, 1.0, w, ui, oi);
1679 }
1680
1681 if (needBlue) {
1682 drawRawCurve(cr, bh, ub, realhistheight, w, h);
1683 cr->set_source_rgb(rgb_B[0], rgb_B[1], rgb_B[2]);
1684 cr->stroke();
1685 drawMarks(cr, bh, 1.0, w, ui, oi);
1686 }
1687
1688 // update stats
1689 for (int c = 0; c < 3; ++c) {
1690 minval[c] = -1;
1691 mincount[c] = 0;
1692 maxval[c] = -1;
1693 maxcount[c] = 0;
1694 peakval[c] = 0;
1695 peakcount[c] = 0;
1696 totcount[c] = 0;
1697 totsum[c] = 0;
1698 }
1699
1700 const auto update =
1701 [&](int c, LUTu &data, unsigned int i) -> void
1702 {
1703 if (i < data.getSize()) {
1704 int cnt = data[i];
1705 if (cnt > 0) {
1706 if (minval[c] < 0) {
1707 minval[c] = i;
1708 mincount[c] = cnt;
1709 }
1710 maxval[c] = i;
1711 maxcount[c] = cnt;
1712 if (peakcount[c] < cnt) {
1713 peakval[c] = i;
1714 peakcount[c] = cnt;
1715 }
1716 ++totcount[c];
1717 }
1718 totsum[c] += cnt;
1719 }
1720 };
1721
1722 for (unsigned int i = 0; i <= ub; ++i) {
1723 update(0, rh, i);
1724 update(1, gh, i);
1725 update(2, bh, i);
1726 }
1727 }
1728
1729
1730 if (valid) {
1731 const auto pct =
1732 [](int n, int d) -> Glib::ustring
1733 {
1734 double p = double(int(double(n)/double(d) * 10000)) / 100.0;
1735 if (p == 0 && n > 0) {
1736 return "<0.01%";
1737 } else {
1738 return Glib::ustring::compose("%1%%", p);
1739 }
1740 };
1741
1742 set_has_tooltip(true);
1743 Glib::ustring tip = "";
1744 for (int i = 0; i < 3; ++i) {
1745 double ev = minval[i] >= 0 ? std::log2(maxval[i] - minval[i]) : 0;
1746 ev = double(int(ev * 100)) / 100.0;
1747 Glib::ustring m = Glib::ustring::compose(M("HISTOGRAM_RAW_STATS_TOOLTIP"), minval[i], pct(mincount[i], totsum[i]), maxval[i], pct(maxcount[i], totsum[i]), peakval[i], pct(peakcount[i], totsum[i]), ev, totcount[i], totsum[i]);
1748 Glib::ustring s = Glib::ustring::compose("<span font_family=\"Arial\" size=\"larger\" foreground=\"%1\">■</span> %2", ch[i], m);
1749 if (i > 0) {
1750 tip += "\n";
1751 }
1752 tip += s;
1753 }
1754 set_tooltip_markup(tip);
1755 } else {
1756 set_has_tooltip(false);
1757 }
1758 }
1759
1760
drawRawCurve(Cairo::RefPtr<Cairo::Context> & cr,LUTu & data,unsigned int ub,double scale,int hsize,int vsize)1761 void HistogramArea::drawRawCurve(Cairo::RefPtr<Cairo::Context> &cr,
1762 LUTu &data, unsigned int ub, double scale,
1763 int hsize, int vsize)
1764 {
1765 double s = RTScalable::getScale();
1766
1767 cr->set_line_width(s);
1768 cr->move_to(0, vsize - 1);
1769 scale = scale <= 0.f ? 0.001f : scale; // avoid division by zero and negative values
1770
1771 unsigned int next = 1;
1772 const bool logscale = drawMode > 0;
1773 const double logmax = std::log2(ub);
1774 //const double ybase = std::pow(10, std::max(std::floor(std::log(scale) / std::log(10))-1, 1.0));
1775 const double ylogmax = std::log10(scale);
1776
1777 RawIdxHelper next_raw_idx(logscale, data.getUpperBound(), hsize);
1778
1779 for (unsigned int i = logscale ? 1 : 0; i < data.getSize(); ) {
1780 double val = 0.0;
1781 for (unsigned int j = i; j < next; ++j) {
1782 val += data[j];
1783 }
1784 val /= (next - i);
1785
1786 val = std::min(val / scale, 1.0);
1787
1788 if (drawMode == 2 && val > 0) { // scale y for log-scale
1789 //val = rtengine::lin2log(val, ybase);
1790 val = std::max((ylogmax + std::log10(val)) / ylogmax, 0.0);
1791 }
1792
1793 double iscaled = std::min(double(i) / ub, 1.0);
1794 if (logscale) { // scale x for log-scale
1795 iscaled = logmax + std::log2(iscaled);
1796 if (iscaled < 0) {
1797 next_raw_idx(next, i);
1798 continue;
1799 }
1800 iscaled /= logmax;
1801 }
1802
1803 double posX = iscaled * hsize;
1804 double posY = vsize - 2 + val * (4 - vsize);
1805
1806 cr->line_to(posX, posY);
1807
1808 next_raw_idx(next, i);
1809 }
1810
1811 cr->line_to(hsize - 1, vsize - 1);
1812 }
1813
1814
on_realize()1815 void HistogramArea::on_realize ()
1816 {
1817
1818 Gtk::DrawingArea::on_realize();
1819 add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK);
1820 }
1821
drawCurve(Cairo::RefPtr<Cairo::Context> & cr,const LUTu & data,double scale,int hsize,int vsize)1822 void HistogramArea::drawCurve(Cairo::RefPtr<Cairo::Context> &cr,
1823 const LUTu &data, double scale, int hsize, int vsize)
1824 {
1825 double s = RTScalable::getScale();
1826
1827 cr->set_line_width(s);
1828 cr->move_to (0, vsize - 1);
1829 scale = scale <= 0.f ? 0.001f : scale; // avoid division by zero and negative values
1830
1831 for (int i = 0; i < 256; i++) {
1832 double val = data[i] * (double)vsize / scale;
1833
1834 if (drawMode == 2) { // scale y for log-scale
1835 val = HistogramScaling::log ((double)vsize, val);
1836 }
1837
1838 double iscaled = i;
1839 if (drawMode > 0) { // scale x for log-scale
1840 iscaled = HistogramScaling::log (255.0, (double)i);
1841 }
1842
1843 double posX = (iscaled / 255.0) * (hsize - 1);
1844 double posY = vsize - 2 + val * (4 - vsize) / vsize;
1845
1846 cr->line_to (posX, posY);
1847 }
1848
1849 cr->line_to (hsize - 1, vsize - 1);
1850 }
1851
drawMarks(Cairo::RefPtr<Cairo::Context> & cr,const LUTu & data,double scale,int hsize,int & ui,int & oi)1852 void HistogramArea::drawMarks(Cairo::RefPtr<Cairo::Context> &cr,
1853 const LUTu &data, double scale, int hsize, int & ui, int & oi)
1854 {
1855 int s = 8 * RTScalable::getScale();
1856
1857 if(data[0] > scale) {
1858 cr->rectangle(0, (ui++)*s, s, s);
1859 }
1860
1861 if(data[data.getUpperBound()] > scale) {
1862 cr->rectangle(hsize - s, (oi++)*s, s, s);
1863 }
1864
1865 cr->fill();
1866 }
1867
drawParade(Cairo::RefPtr<Cairo::Context> & cr,int w,int h)1868 void HistogramArea::drawParade(Cairo::RefPtr<Cairo::Context> &cr, int w, int h)
1869 {
1870 // Arbitrary scale factor divided by current scale.
1871 const float scale = trace_brightness * 32.f * 255.f / waveform_scale;
1872 const int wave_width = rwave.width();
1873 const int wave_height = rwave.height();
1874
1875 // See Cairo documentation on stride.
1876 const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, rwave.width());
1877 const auto buffer_size = static_cast<std::vector<unsigned char>::size_type>(wave_height) * cairo_stride;
1878
1879 if (parade_buffer_r_dirty && needRed) {
1880 parade_buffer_r.assign(buffer_size, 0);
1881 assert(parade_buffer_r.size() % 4 == 0);
1882
1883 for (int val = 0; val < wave_height; val++) {
1884 const int* const r_row = rwave[val];
1885 std::uint32_t* const buffer_r_row = reinterpret_cast<uint32_t*>(parade_buffer_r.data() + (255 - val) * cairo_stride);
1886 for (int col = 0; col < wave_width; col++) {
1887 const unsigned char r = std::min<float>(scale * r_row[col], 0xff);
1888 if (r != 0) {
1889 buffer_r_row[col] = (r << 16) | (r << 24);
1890 }
1891 }
1892 }
1893
1894 parade_buffer_r_dirty = false;
1895 }
1896
1897 if (parade_buffer_g_dirty && needGreen) {
1898 parade_buffer_g.assign(buffer_size, 0);
1899 assert(parade_buffer_g.size() % 4 == 0);
1900
1901 for (int val = 0; val < wave_height; val++) {
1902 const int* const g_row = gwave[val];
1903 std::uint32_t* const buffer_g_row = reinterpret_cast<uint32_t*>(parade_buffer_g.data() + (255 - val) * cairo_stride);
1904 for (int col = 0; col < wave_width; col++) {
1905 const unsigned char g = std::min<float>(scale * g_row[col], 0xff);
1906 if (g != 0) {
1907 buffer_g_row[col] = (g << 8) | (g << 24);
1908 }
1909 }
1910 }
1911
1912 parade_buffer_g_dirty = false;
1913 }
1914
1915 if (parade_buffer_b_dirty && needBlue) {
1916 parade_buffer_b.assign(buffer_size, 0);
1917 assert(parade_buffer_b.size() % 4 == 0);
1918
1919 for (int val = 0; val < wave_height; val++) {
1920 const int* const b_row = bwave[val];
1921 std::uint32_t* const buffer_b_row = reinterpret_cast<uint32_t*>(parade_buffer_b.data() + (255 - val) * cairo_stride);
1922 for (int col = 0; col < wave_width; col++) {
1923 const unsigned char b = std::min<float>(scale * b_row[col], 0xff);
1924 if (b != 0) {
1925 const unsigned char green = b * rgb_B[1]; // Make blue easier to see.
1926 buffer_b_row[col] = b | (green << 8) | (b << 24);
1927 }
1928 }
1929 }
1930
1931 parade_buffer_b_dirty = false;
1932 }
1933
1934 if (wave_buffer_luma_dirty && needLuma) {
1935 wave_buffer_luma.assign(buffer_size, 0);
1936 assert(wave_buffer_luma.size() % 4 == 0);
1937
1938 for (int val = 0; val < wave_height; val++) {
1939 const int* const l_row = lwave[val];
1940 std::uint32_t* const buffer_row =
1941 reinterpret_cast<uint32_t*>(wave_buffer_luma.data() + (255 - val) * cairo_stride);
1942 for (int col = 0; col < wave_width; col++) {
1943 const unsigned char l = std::min<float>(scale * l_row[col], 0xff);
1944 buffer_row[col] = l | (l << 8) | (l << 16) | (l << 24);
1945 }
1946 }
1947
1948 wave_buffer_luma_dirty = false;
1949 }
1950
1951 std::vector<unsigned char*> buffers;
1952 if (needLuma) {
1953 buffers.push_back(wave_buffer_luma.data());
1954 }
1955 if (needRed) {
1956 buffers.push_back(parade_buffer_r.data());
1957 }
1958 if (needGreen) {
1959 buffers.push_back(parade_buffer_g.data());
1960 }
1961 if (needBlue) {
1962 buffers.push_back(parade_buffer_b.data());
1963 }
1964
1965 auto orig_matrix = cr->get_matrix();
1966 const double display_wave_width = static_cast<double>(w) / buffers.size();
1967 for (unsigned i = 0; i < buffers.size(); i++) {
1968 Cairo::RefPtr<Cairo::ImageSurface> surface;
1969 cr->translate(i * display_wave_width, padding);
1970 cr->scale(display_wave_width / wave_width, (h - 2 * padding) / wave_height);
1971 surface = Cairo::ImageSurface::create(
1972 buffers[i], Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
1973 cr->set_source(surface, 0, 0);
1974 cr->set_operator(Cairo::OPERATOR_OVER);
1975 cr->paint();
1976 surface->finish();
1977 cr->set_matrix(orig_matrix);
1978 }
1979 }
1980
drawVectorscope(Cairo::RefPtr<Cairo::Context> & cr,int w,int h)1981 void HistogramArea::drawVectorscope(Cairo::RefPtr<Cairo::Context> &cr, int w, int h)
1982 {
1983 if (scopeType != ScopeType::VECTORSCOPE_HC && scopeType != ScopeType::VECTORSCOPE_HS) {
1984 return;
1985 }
1986
1987 const auto& vect = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc : vect_hs;
1988 auto& vect_buffer = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc_buffer : vect_hs_buffer;
1989 auto& vect_buffer_dirty = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc_buffer_dirty : vect_hs_buffer_dirty;
1990
1991 const int vect_width = vect.width();
1992 const int vect_height = vect.height();
1993 // Arbitrary scale factor multiplied by vectorscope area and divided by
1994 // current scale.
1995 const float scale = trace_brightness * 8.f * vect_width * vect_height / vectorscope_scale;
1996
1997 // See Cairo documentation on stride.
1998 const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, vect_width);
1999
2000 if (vect_buffer_dirty && vectorscope_scale > 0) {
2001 if (vect_buffer.size() != static_cast<std::size_t>(cairo_stride) * vect_height) {
2002 vect_buffer.resize(static_cast<std::size_t>(cairo_stride) * vect_height);
2003 }
2004
2005 assert(vect_buffer.size() % 4 == 0);
2006
2007 for (int y = 0; y < vect_height; y++) {
2008 const int* const vect_row = vect[y];
2009 std::uint32_t* const buffer_row =
2010 reinterpret_cast<uint32_t*>(vect_buffer.data() + (vect_height - 1 - y) * cairo_stride);
2011 for (int x = 0; x < vect_width; x++) {
2012 const unsigned char value = std::min<float>(scale * vect_row[x], 0xff);
2013 buffer_row[x] = value | (value << 8) | (value << 16) | (value << 24);
2014 }
2015 }
2016
2017 vect_buffer_dirty = false;
2018 }
2019
2020 const bool fit_width =
2021 vect_width * (h - 2 * padding) > vect_height * (w - 2 * padding);
2022 const float scope_scale = fit_width ?
2023 (w - 2 * padding) / vect_width : (h - 2 * padding) / vect_height;
2024 const float scope_size = (vectorscope_scale > 0) ?
2025 scope_scale * std::max<double>(vect_width, vect_height) : std::min<float>(w, h) - 2 * padding;
2026 const float o_x = (w - scope_scale * vect_width) / 2;
2027 const float o_y = (h - scope_scale * vect_height) / 2;
2028 const double s = RTScalable::getScale();
2029 auto orig_matrix = cr->get_matrix();
2030 const double line_length = scope_size / 2.0;
2031 std::valarray<double> ch_ds(1);
2032
2033 cr->translate(w / 2.0, h / 2.0);
2034 cr->set_line_width (1.0 * s);
2035 cr->set_antialias(Cairo::ANTIALIAS_SUBPIXEL);
2036 ch_ds[0] = 4;
2037
2038 if (scopeType == ScopeType::VECTORSCOPE_HS) { // Hue-Saturation.
2039 // RYGCBM lines.
2040 cr->set_line_width (2.0 * s);
2041 constexpr double color_labels[6][3] = {
2042 {1, 0, 0}, // R
2043 {0, 1, 0}, // G
2044 {0, 0, 1}, // B
2045 {0, 1, 1}, // C
2046 {1, 0, 1}, // M
2047 {1, 1, 0}, // Y
2048 };
2049 for (int i = 0; i < 3; i++) {
2050 auto gradient = Cairo::LinearGradient::create(-line_length, 0, line_length, 0);
2051 const double (&color_1)[3] = color_labels[i];
2052 const double (&color_2)[3] = color_labels[i + 3];
2053 cr->set_source(gradient);
2054 gradient->add_color_stop_rgba(0, color_2[0], color_2[1], color_2[2], 0.5);
2055 gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25);
2056 gradient->add_color_stop_rgba(1, color_1[0], color_1[1], color_1[2], 0.5);
2057 cr->move_to(-line_length, 0);
2058 cr->line_to(line_length, 0);
2059 cr->rotate_degrees(-120);
2060 cr->stroke();
2061 }
2062 cr->set_line_width (1.0 * s);
2063 cr->set_source_rgba (1, 1, 1, 0.25);
2064 // 100% saturation circle.
2065 cr->arc(0, 0, scope_size / 2.0, 0, 2 * RT_PI);
2066 cr->stroke();
2067 // 25%, 50%, and 75% saturation.
2068 cr->set_dash(ch_ds, 0);
2069 for (int i = 1; i < 4; i++) {
2070 cr->arc(0, 0, i * scope_size / 8.0, 0, 2 * RT_PI);
2071 cr->stroke();
2072 }
2073 // HSV skin tone line derived from -I axis of YIQ.
2074 cr->rotate(-0.134900 * RT_PI);
2075 cr->move_to(0, 0);
2076 cr->line_to(line_length, 0);
2077 cr->stroke();
2078 cr->unset_dash();
2079 } else if (scopeType == ScopeType::VECTORSCOPE_HC) { // Hue-Chroma.
2080 // a and b axes.
2081 Cairo::RefPtr<Cairo::LinearGradient> gradient;
2082 cr->set_line_width (2.0 * s);
2083 gradient = Cairo::LinearGradient::create(0, -line_length, 0, line_length);
2084 cr->set_source(gradient);
2085 gradient->add_color_stop_rgba(0, 1, 1, 0, 0.5); // "yellow"
2086 gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25); // neutral
2087 gradient->add_color_stop_rgba(1, 0, 0, 1, 0.5); // "blue"
2088 cr->move_to(0, 0);
2089 cr->line_to(0, line_length);
2090 cr->move_to(0, 0);
2091 cr->line_to(0, -line_length);
2092 cr->stroke();
2093 gradient = Cairo::LinearGradient::create(-line_length, 0, line_length, 0);
2094 cr->set_source(gradient);
2095 gradient->add_color_stop_rgba(0, 0, 1, 0, 0.5); // "green"
2096 gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25); // neutral
2097 gradient->add_color_stop_rgba(1, 1, 0, 1, 0.5); // "magenta"
2098 cr->move_to(0, 0);
2099 cr->line_to(line_length, 0);
2100 cr->move_to(0, 0);
2101 cr->line_to(-line_length, 0);
2102 cr->stroke();
2103 cr->set_source_rgba (1, 1, 1, 0.25);
2104 cr->set_line_width (1.0 * s);
2105 // 25%, 50%, 75%, and 100% of standard chroma range.
2106 cr->set_dash(ch_ds, 0);
2107 for (int i = 1; i <= 4; i++) {
2108 cr->arc(0, 0, i * scope_size / 8.0, 0, 2 * RT_PI);
2109 cr->stroke();
2110 }
2111 // CIELAB skin tone line, approximated by 50% saturation and
2112 // value along the HSV skin tone line.
2113 cr->rotate(-0.321713 * RT_PI);
2114 cr->move_to(0, 0);
2115 cr->line_to(line_length, 0);
2116 cr->stroke();
2117 cr->unset_dash();
2118 }
2119 cr->set_matrix(orig_matrix);
2120
2121 // Vectorscope trace.
2122 if (vectorscope_scale > 0) {
2123 Cairo::RefPtr<Cairo::ImageSurface> surface = Cairo::ImageSurface::create(
2124 vect_buffer.data(), Cairo::FORMAT_ARGB32, vect_width, vect_height, cairo_stride);
2125 cr->translate(o_x, o_y);
2126 cr->scale(scope_scale, scope_scale);
2127 cr->set_source(surface, 0, 0);
2128 cr->set_operator(Cairo::OPERATOR_OVER);
2129 cr->paint();
2130 surface->finish();
2131 cr->set_matrix(orig_matrix);
2132
2133 if (needPointer && pointer_red >= 0 && pointer_green >= 0 && pointer_blue >= 0) {
2134 float cx, cy;
2135 if (scopeType == ScopeType::VECTORSCOPE_HS) {
2136 float H, S, L;
2137 Color::rgb2hslfloat(pointer_red * 257.f, pointer_green * 257.f, pointer_blue * 257.f, H, S, L);
2138 cx = (w + scope_size * S * std::cos(H * 2 * RT_PI_F)) / 2;
2139 cy = (h - scope_size * S * std::sin(H * 2 * RT_PI_F)) / 2;
2140 } else {
2141 constexpr float ab_factor = 1.f / 256.f;
2142 cx = w / 2.f + scope_size * pointer_a * ab_factor;
2143 cy = h / 2.f - scope_size * pointer_b * ab_factor;
2144 }
2145 const float crosshair_size = 20.f * s;
2146 cr->set_source_rgba(1, 1, 1, 0.5);
2147 cr->move_to(cx - crosshair_size, cy);
2148 cr->line_to(cx + crosshair_size, cy);
2149 cr->move_to(cx, cy - crosshair_size);
2150 cr->line_to(cx, cy + crosshair_size);
2151 cr->stroke();
2152 cr->arc(cx, cy, 3 * s, 0, 2 * RT_PI);
2153 cr->set_source_rgb(1, 1, 1);
2154 cr->fill_preserve();
2155 cr->set_source_rgb(0, 0, 0);
2156 cr->set_line_width (1.0 * s);
2157 cr->stroke();
2158 }
2159 }
2160 }
2161
drawWaveform(Cairo::RefPtr<Cairo::Context> & cr,int w,int h)2162 void HistogramArea::drawWaveform(Cairo::RefPtr<Cairo::Context> &cr, int w, int h)
2163 {
2164 // Arbitrary scale factor divided by current scale.
2165 const float scale = trace_brightness * 32.f * 255.f / waveform_scale;
2166 const int wave_width = rwave.width();
2167 const int wave_height = rwave.height();
2168
2169 // See Cairo documentation on stride.
2170 const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, rwave.width());
2171 const auto buffer_size = static_cast<std::vector<unsigned char>::size_type>(wave_height) * cairo_stride;
2172
2173 if (wave_buffer_dirty && (needRed || needGreen || needBlue)) {
2174 wave_buffer.assign(buffer_size, 0);
2175 assert(wave_buffer.size() % 4 == 0);
2176
2177 for (int val = 0; val < wave_height; val++) {
2178 const int* const r_row = rwave[val];
2179 const int* const g_row = gwave[val];
2180 const int* const b_row = bwave[val];
2181 std::uint32_t* const buffer_row = reinterpret_cast<uint32_t*>(wave_buffer.data() + (255 - val) * cairo_stride);
2182 for (int col = 0; col < wave_width; col++) {
2183 const unsigned char r = needRed ? std::min<float>(scale * r_row[col], 0xff) : 0;
2184 const unsigned char b = needBlue ? std::min<float>(scale * b_row[col], 0xff) : 0;
2185 const unsigned char g = needGreen ? rtengine::LIM(std::min<float>(scale * g_row[col], 0xff) + float(needBlue ? b * rgb_B[1] : 0), 0.f, float(0xff)) : 0;
2186 const unsigned char value = rtengine::max(r, g, b);
2187 if (value != 0) {
2188 // Ensures correct order regardless of endianness.
2189 buffer_row[col] = b | (g << 8) | (r << 16) | (value << 24);
2190 }
2191 }
2192 }
2193
2194 wave_buffer_dirty = false;
2195 }
2196
2197 if (wave_buffer_luma_dirty && needLuma) {
2198 wave_buffer_luma.assign(buffer_size, 0);
2199 assert(wave_buffer_luma.size() % 4 == 0);
2200
2201 for (int val = 0; val < wave_height; val++) {
2202 const int* const l_row = lwave[val];
2203 std::uint32_t* const buffer_row =
2204 reinterpret_cast<uint32_t*>(wave_buffer_luma.data() + (255 - val) * cairo_stride);
2205 for (int col = 0; col < wave_width; col++) {
2206 const unsigned char l = std::min<float>(scale * l_row[col], 0xff);
2207 buffer_row[col] = l | (l << 8) | (l << 16) | (l << 24);
2208 }
2209 }
2210
2211 wave_buffer_luma_dirty = false;
2212 }
2213
2214 Cairo::RefPtr<Cairo::ImageSurface> surface;
2215 auto orig_matrix = cr->get_matrix();
2216 cr->translate(0, padding);
2217 cr->scale(static_cast<double>(w) / wave_width, (h - 2 * padding) / wave_height);
2218 if (needLuma) {
2219 surface = Cairo::ImageSurface::create(
2220 wave_buffer_luma.data(), Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
2221 cr->set_source(surface, 0, 0);
2222 cr->set_operator(Cairo::OPERATOR_OVER);
2223 cr->paint();
2224 surface->finish();
2225 }
2226 if (needRed || needGreen || needBlue) {
2227 surface = Cairo::ImageSurface::create(
2228 wave_buffer.data(), Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
2229 cr->set_source(surface, 0, 0);
2230 cr->set_operator(Cairo::OPERATOR_OVER);
2231 cr->paint();
2232 surface->finish();
2233 }
2234 cr->set_matrix(orig_matrix);
2235 }
2236
on_draw(const::Cairo::RefPtr<Cairo::Context> & cr)2237 bool HistogramArea::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
2238 {
2239
2240 if (!updatePending() && (get_width() != oldwidth || get_height() != oldheight || isDirty())) {
2241 updateBackBuffer ();
2242 }
2243
2244 const Glib::RefPtr<Gtk::StyleContext> style = get_style_context();
2245 style->render_background(cr, 0, 0, get_width(), get_height());
2246 copySurface(cr, NULL);
2247 style->render_frame (cr, 0, 0, get_width(), get_height());
2248
2249 return true;
2250 }
2251
on_button_press_event(GdkEventButton * event)2252 bool HistogramArea::on_button_press_event (GdkEventButton* event)
2253 {
2254 if (!is_main_) {
2255 return true;
2256 }
2257
2258 isPressed = true;
2259 movingPosition = event->x;
2260
2261 if (event->type == GDK_2BUTTON_PRESS && event->button == 1
2262 && (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW)
2263 ) {
2264
2265 drawMode = (drawMode + 1) % 3;
2266 options.histogramDrawMode = (options.histogramDrawMode + 1) % 3;
2267
2268 if (myDrawModeListener) {
2269 myDrawModeListener->toggleButtonMode ();
2270 }
2271
2272 updateBackBuffer ();
2273 queue_draw ();
2274 }
2275
2276 return true;
2277 }
2278
on_button_release_event(GdkEventButton * event)2279 bool HistogramArea::on_button_release_event (GdkEventButton* event)
2280 {
2281 if (!is_main_) {
2282 return true;
2283 }
2284
2285 isPressed = false;
2286 return true;
2287 }
2288
on_motion_notify_event(GdkEventMotion * event)2289 bool HistogramArea::on_motion_notify_event (GdkEventMotion* event)
2290 {
2291 if (!is_main_) {
2292 return true;
2293 }
2294
2295 if (
2296 drawMode == 0
2297 && (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW)
2298 ) {
2299 return false;
2300 }
2301
2302 if (!isPressed) {
2303 return true;
2304 }
2305
2306 if (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW) { // Adjust log scale.
2307 double mod = 1 + (event->x - movingPosition) / get_width();
2308
2309 factor /= mod;
2310 if (factor < 1.0)
2311 factor = 1.0;
2312 if (factor > 100.0)
2313 factor = 100.0;
2314
2315 sigFactorChanged.emit(factor);
2316
2317 setDirty(true);
2318 queue_draw ();
2319 } else if (
2320 scopeType == ScopeType::PARADE
2321 || scopeType == ScopeType::WAVEFORM
2322 || scopeType == ScopeType::VECTORSCOPE_HC
2323 || scopeType == ScopeType::VECTORSCOPE_HS
2324 ) { // Adjust brightness.
2325 constexpr float RANGE = MAX_BRIGHT / MIN_BRIGHT;
2326 double dx = (event->x - movingPosition) / get_width();
2327 float new_brightness = LIM<float>(trace_brightness * pow(RANGE, dx), MIN_BRIGHT, MAX_BRIGHT);
2328 setBrightness(new_brightness);
2329 movingPosition = event->x;
2330 }
2331
2332 return true;
2333 }
2334
getBrightness(void)2335 float HistogramArea::getBrightness(void)
2336 {
2337 return trace_brightness;
2338 }
2339
setBrightness(float brightness)2340 void HistogramArea::setBrightness(float brightness)
2341 {
2342 brightness = LIM<float>(brightness, MIN_BRIGHT, MAX_BRIGHT);
2343 if (brightness != trace_brightness) {
2344 parade_buffer_r_dirty = parade_buffer_g_dirty = parade_buffer_b_dirty = wave_buffer_dirty = wave_buffer_luma_dirty = vect_hc_buffer_dirty = vect_hs_buffer_dirty = true;
2345 trace_brightness = brightness;
2346 setDirty(true);
2347 queue_draw();
2348
2349 signal_brightness_changed.emit(trace_brightness);
2350 }
2351 }
2352
getBrighnessChangedSignal(void)2353 HistogramArea::SignalBrightnessChanged HistogramArea::getBrighnessChangedSignal(void)
2354 {
2355 return signal_brightness_changed;
2356 }
2357
signal_factor_changed()2358 HistogramArea::type_signal_factor_changed HistogramArea::signal_factor_changed()
2359 {
2360 return sigFactorChanged;
2361 }
2362