4 * Copyright (C) 1997-2011 Adam Williams <broadcast at earthling dot net>
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "bcdisplayinfo.h"
23 #include "bcsignals.h"
26 #include "histogram.h"
27 #include "histogramconfig.h"
28 #include "histogramwindow.h"
31 #include "localsession.h"
33 #include "pluginserver.h"
41 HistogramWindow::HistogramWindow(HistogramMain *plugin)
42 : PluginClientWindow(plugin,
49 this->plugin = plugin;
53 HistogramWindow::~HistogramWindow()
58 void HistogramWindow::create_objects()
60 int margin = plugin->get_theme()->widget_border;
61 int x = margin, y = margin, x1 = margin;
63 add_subwindow(mode_v = new HistogramMode(plugin,
68 x += mode_v->get_w() + margin;
69 add_subwindow(mode_r = new HistogramMode(plugin,
74 x += mode_r->get_w() + margin;
75 add_subwindow(mode_g = new HistogramMode(plugin,
80 x += mode_g->get_w() + margin;
81 add_subwindow(mode_b = new HistogramMode(plugin,
88 x = get_w() - margin - plugin->get_theme()->get_image_set("histogram_rgb_toggle")[0]->get_w();
89 add_subwindow(parade_on = new HistogramParade(plugin,
94 x -= parade_on->get_w() + margin;
95 add_subwindow(parade_off = new HistogramParade(plugin,
103 y += parade_on->get_h() + margin;
104 add_subwindow(canvas_title1 = new BC_Title(margin,
107 add_subwindow(canvas_title2 = new BC_Title(get_w() - get_text_width(MEDIUMFONT, "110%") - margin,
111 y += canvas_title2->get_h() + margin;
113 canvas_h = get_h() - y - yS(210);
116 add_subwindow(low_input_carrot = new HistogramCarrot(plugin,
121 x = low_input_carrot->get_w() / 2 + x;
122 canvas_w = get_w() - x - x;
125 title2_x = x + (int)(canvas_w * -HIST_MIN_INPUT / FLOAT_RANGE);
126 title3_x = x + (int)(canvas_w * (1.0 - HIST_MIN_INPUT) / FLOAT_RANGE);
127 title4_x = x + (int)(canvas_w);
133 add_subwindow(canvas = new HistogramCanvas(plugin,
141 draw_3d_border(x - 2,
150 // Calculate output curve with no value function
151 plugin->tabulate_curve(plugin->mode, 0);
153 y += canvas->get_h();
156 add_subwindow(gamma_carrot = new HistogramCarrot(plugin,
159 canvas->get_w() / 2 -
160 low_input_carrot->get_w() / 2 ,
163 add_subwindow(high_input_carrot = new HistogramCarrot(plugin,
167 low_input_carrot->get_w() / 2,
169 y += low_input_carrot->get_h() + margin;
172 // add_subwindow(title = new BC_Title(x, y, _("Input:")));
173 // x += title->get_w() + margin;
174 low_input = new HistogramText(plugin, this, x, y);
175 low_input->create_objects();
177 x = get_w() / 2 - low_input->get_w() / 2;
178 gamma = new HistogramText(plugin, this, x, y, 0.01, 100.);
179 gamma->create_objects();
181 x = get_w() - low_input->get_w() - margin;
182 high_input = new HistogramText(plugin, this, x, y);
183 high_input->create_objects();
185 y += high_input->get_h() + margin;
188 add_subwindow(output = new HistogramSlider(plugin, this,
189 canvas->get_x(), y, canvas->get_w(), yS(20), 0));
193 draw_3d_border(output->get_x() - 2, output->get_y() - 2,
194 output->get_w() + 4, output->get_h() + 4,
195 get_bg_color(), BLACK, MDGREY, get_bg_color());
196 y += output->get_h();
198 add_subwindow(low_output_carrot = new HistogramCarrot(plugin,
201 add_subwindow(high_output_carrot = new HistogramCarrot(plugin,
202 this, canvas->get_x() + canvas->get_w() -
203 low_output_carrot->get_w() / 2, y));
204 y += high_output_carrot->get_h() + margin;
207 // add_subwindow(title = new BC_Title(x, y, _("Output:")));
208 // x += title->get_w() + margin;
209 low_output = new HistogramText(plugin, this, x, y);
210 low_output->create_objects();
212 const char *linear_text = _("Linear");
213 int xs = get_w()/2 - xS(50);
214 x = xs - BC_Title::calculate_w(this, linear_text) - margin;
215 add_subwindow(log_title1 = new BC_Title(x, y, linear_text));
216 add_subwindow(log_slider = new HistogramLogSlider(plugin, this, xs, y));
217 xs += log_slider->get_w() + margin;
218 add_subwindow(log_title2 = new BC_Title(xs, y, _("Log")));
220 high_output = new HistogramText(plugin, this,
221 get_w() - low_output->get_w() - margin, y);
222 high_output->create_objects();
225 y += high_output->get_h() + margin;
227 add_subwindow(bar = new BC_Bar(x, y, get_w() - margin * 2));
228 y += bar->get_h() + margin;
230 add_subwindow(automatic = new HistogramAuto(plugin, x, y));
232 add_subwindow(threshold_title = new BC_Title(x2, y, _("Threshold:")));
234 threshold = new HistogramText(plugin, this, x3, y);
235 threshold->create_objects();
236 y += automatic->get_h() + margin;
238 add_subwindow(plot = new HistogramPlot(plugin, x1, y));
239 add_subwindow(select = new HistogramSelect(plugin, this, x2, y));
240 frames = new HistogramFrames(plugin, this, x3, y);
241 frames->create_objects();
242 x = x3 + frames->get_w() + margin;
243 add_subwindow(clear_frames = new HistogramClearFrames(plugin, this, x, y));
244 y += plot->get_h() + margin;
247 add_subwindow(split = new HistogramSplit(plugin, x, y));
249 add_subwindow(reset = new HistogramReset(plugin, x, y + yS(5)));
259 int HistogramWindow::resize_event(int w, int h)
261 int xdiff = w - get_w();
262 int ydiff = h - get_h();
264 parade_on->reposition_window(parade_on->get_x() + xdiff,
266 parade_off->reposition_window(parade_off->get_x() + xdiff,
268 canvas_title2->reposition_window(canvas_title2->get_x() + xdiff,
269 canvas_title2->get_y());
271 // Canvas follows window size
272 canvas_w = canvas_w + xdiff;
273 canvas_h = canvas_h + ydiff;
274 canvas->reposition_window(canvas->get_x(),
280 draw_3d_border(canvas->get_x() - 2,
289 low_input_carrot->reposition_window(low_input_carrot->get_x(),
290 low_input_carrot->get_y() + ydiff);
291 gamma_carrot->reposition_window(gamma_carrot->get_x(),
292 gamma_carrot->get_y() + ydiff);
293 high_input_carrot->reposition_window(high_input_carrot->get_x(),
294 high_input_carrot->get_y() + ydiff);
296 low_input->reposition_window(low_input->get_x(),
297 low_input->get_y() + ydiff);
298 gamma->reposition_window(w / 2 - gamma->get_w() / 2,
299 gamma->get_y() + ydiff);
300 high_input->reposition_window(high_input->get_x() + xdiff,
301 low_input->get_y() + ydiff);
303 output->reposition_window(output->get_x(),
304 output->get_y() + ydiff,
305 output->get_w() + xdiff,
310 draw_3d_border(output->get_x() - 2,
319 low_output_carrot->reposition_window(low_output_carrot->get_x(),
320 low_output_carrot->get_y() + ydiff);
321 high_output_carrot->reposition_window(high_output_carrot->get_x(),
322 high_output_carrot->get_y() + ydiff);
324 low_output->reposition_window(low_output->get_x(),
325 low_output->get_y() + ydiff);
326 int xs = (get_w() - log_slider->get_w()) / 2;
327 int margin = plugin->get_theme()->widget_border;
328 log_title1->reposition_window(xs - log_title1->get_w() - margin,
329 log_title1->get_y() + ydiff);
330 log_slider->reposition_window(xs,
331 log_slider->get_y() + ydiff);
332 log_title2->reposition_window(xs + log_slider->get_w() + margin,
333 log_title2->get_y() + ydiff);
334 high_output->reposition_window(high_output->get_x() + xdiff,
335 high_output->get_y() + ydiff);
337 bar->reposition_window(bar->get_x(),
338 bar->get_y() + ydiff,
339 bar->get_w() + xdiff);
341 automatic->reposition_window(automatic->get_x(),
342 automatic->get_y() + ydiff);
343 threshold_title->reposition_window(threshold_title->get_x(),
344 threshold_title->get_y() + ydiff);
345 threshold->reposition_window(threshold->get_x(),
346 threshold->get_y() + ydiff);
347 plot->reposition_window(plot->get_x(),
348 plot->get_y() + ydiff);
350 split->reposition_window(split->get_x(),
351 split->get_y() + ydiff);
352 reset->reposition_window(reset->get_x(),
353 reset->get_y() + ydiff);
355 frames->reposition_window(frames->get_x(),
356 frames->get_y() + ydiff);
357 select->reposition_window(select->get_x(),
358 select->get_y() + ydiff);
359 clear_frames->reposition_window(clear_frames->get_x(),
360 clear_frames->get_y() + ydiff);
372 int HistogramWindow::keypress_event()
379 for(int i = 0; i < HISTOGRAM_MODES; i++)
381 if(active_value == &plugin->config.gamma[i])
385 if(get_keypress() == RIGHT || get_keypress() == UP)
387 *active_value += sign * PRECISION;
388 plugin->config.boundaries();
390 plugin->send_configure_change();
394 if(get_keypress() == LEFT || get_keypress() == DOWN)
396 *active_value -= sign * PRECISION;
397 plugin->config.boundaries();
399 plugin->send_configure_change();
407 void HistogramWindow::update(int do_canvases,
414 automatic->update(plugin->config.automatic);
415 mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
416 mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
417 mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
418 mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
419 plot->update(plugin->config.plot);
420 split->update(plugin->config.split);
421 frames->update(plugin->config.frames);
422 parade_on->update(plugin->parade ? 1 : 0);
423 parade_off->update(plugin->parade ? 0 : 1);
424 log_slider->update(plugin->config.log_slider);
435 low_input_carrot->update();
436 high_input_carrot->update();
437 gamma_carrot->update();
438 low_output_carrot->update();
439 high_output_carrot->update();
446 high_input->update();
447 low_output->update();
448 high_output->update();
456 void HistogramWindow::draw_canvas_mode(int mode, int color, int y, int h)
459 int max = 0, *accum = plugin->accum[mode];
461 for( int i=0; i<HISTOGRAM_SLOTS; ++i ) {
463 if( max < v ) max = v;
468 double log_slider = plugin->config.log_slider;
469 double lin_slider = 1. - log_slider;
470 double lin_scale = (lin_slider * h) / max;
471 double log_scale = (log_slider * h) / log(max);
472 for( int i=0,x=0; x<canvas_w; ++x ) {
474 int i1 = (HISTOGRAM_SLOTS * (x+1)) / canvas_w;
479 m = m*lin_scale + log(m)*log_scale;
481 canvas->set_color(BLACK);
482 canvas->draw_line(x, y, x, y+h - m);
483 canvas->set_color(color);
484 canvas->draw_line(x, y+h - m, x, y+h);
488 canvas->set_color(BLACK);
489 canvas->draw_box(0, y, canvas_w, h);
493 canvas->set_color(WHITE);
494 canvas->set_line_width(2);
498 for( int x=0; x<canvas_w; ++x ) {
499 float input = (float)x / canvas_w * FLOAT_RANGE + HIST_MIN_INPUT;
500 float output = plugin->calculate_level(input, mode, 0);
501 int y2 = h - (int)(output * h);
503 canvas->draw_line(x-1, y+y1, x, y+y2);
507 canvas->set_line_width(1);
511 void HistogramWindow::update_canvas()
515 draw_canvas_mode(HISTOGRAM_RED, RED, 0, canvas_h / 3);
516 draw_canvas_mode(HISTOGRAM_GREEN, GREEN, canvas_h / 3, canvas_h / 3);
517 draw_canvas_mode(HISTOGRAM_BLUE, BLUE, canvas_h * 2 / 3, canvas_h - canvas_h * 2 / 3);
521 draw_canvas_mode(plugin->mode, MEGREY, 0, canvas_h);
525 // Draw 0 and 100% lines.
526 canvas->set_color(RED);
527 int x = (int)(canvas_w * -HIST_MIN_INPUT / FLOAT_RANGE);
532 x = (int)(canvas_w * (1.0 - HIST_MIN_INPUT) / FLOAT_RANGE);
543 HistogramParade::HistogramParade(HistogramMain *plugin,
544 HistogramWindow *gui,
550 value ? plugin->get_theme()->get_image_set("histogram_rgb_toggle") :
551 plugin->get_theme()->get_image_set("histogram_toggle"),
554 this->plugin = plugin;
558 set_tooltip(_("RGB Parade on"));
560 set_tooltip(_("RGB Parade off"));
563 int HistogramParade::handle_event()
566 plugin->parade = value;
580 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
581 HistogramWindow *gui,
592 this->plugin = plugin;
596 int HistogramCanvas::button_press_event()
599 if(is_event_win() && cursor_inside())
601 if(!plugin->dragging_point &&
602 (!plugin->config.automatic || plugin->mode == HISTOGRAM_VALUE))
610 int HistogramCanvas::cursor_motion_event()
612 if(is_event_win() && cursor_inside())
618 int HistogramCanvas::button_release_event()
624 HistogramReset::HistogramReset(HistogramMain *plugin, int x, int y)
625 : BC_GenericButton(x, y, _("Reset"))
627 this->plugin = plugin;
629 int HistogramReset::handle_event()
631 plugin->config.reset(0);
632 ((HistogramWindow*)plugin->thread->window)->update(1, 1, 1, 1);
633 plugin->send_configure_change();
638 HistogramSelect::HistogramSelect(HistogramMain *plugin, HistogramWindow *gui,
640 : BC_GenericButton(x, y, xS(100), _("Frames"))
642 this->plugin = plugin;
644 set_tooltip(_("Set frames to selection duration"));
646 int HistogramSelect::handle_event()
648 MWindow *mwindow = plugin->server->mwindow;
650 EDL *edl = mwindow->edl;
651 double start = edl->local_session->get_selectionstart();
652 int64_t start_pos = edl->get_frame_rate() * start;
653 double end = edl->local_session->get_selectionend();
654 int64_t end_pos = edl->get_frame_rate() * end;
655 int64_t frames = end_pos - start_pos;
656 gui->frames->update(frames);
657 plugin->config.frames = frames;
658 plugin->send_configure_change();
663 HistogramClearFrames::HistogramClearFrames(HistogramMain *plugin, HistogramWindow *gui,
665 : BC_Button(x, y, plugin->get_theme()->get_image_set("reset_button"))
667 this->plugin = plugin;
669 set_tooltip(_("Clear frames"));
672 int HistogramClearFrames::handle_event()
674 plugin->config.frames = 0;
675 gui->frames->update(0);
676 plugin->send_configure_change();
680 HistogramLogSlider::HistogramLogSlider(HistogramMain *plugin, HistogramWindow *gui,
682 : BC_FSlider(x, y, 0, xS(100), xS(100), 0., 1., plugin->config.log_slider)
685 this->plugin = plugin;
689 int HistogramLogSlider::handle_event()
691 plugin->config.log_slider = get_value();
692 plugin->send_configure_change();
696 HistogramCarrot::HistogramCarrot(HistogramMain *plugin, HistogramWindow *gui, int x, int y)
697 : BC_Toggle(x, y, plugin->get_theme()->get_image_set("histogram_carrot"), 0)
699 this->plugin = plugin;
704 HistogramCarrot::~HistogramCarrot()
708 float* HistogramCarrot::get_value()
710 if( this == gui->low_input_carrot )
711 return &plugin->config.low_input[plugin->mode];
712 if( this == gui->high_input_carrot )
713 return &plugin->config.high_input[plugin->mode];
714 if( this == gui->gamma_carrot )
715 return &plugin->config.gamma[plugin->mode];
716 if( this == gui->low_output_carrot )
717 return &plugin->config.low_output[plugin->mode];
718 if( this == gui->high_output_carrot )
719 return &plugin->config.high_output[plugin->mode];
723 void HistogramCarrot::update()
726 float *value = get_value();
728 if(this != gui->gamma_carrot)
730 new_x = (int)(gui->canvas->get_x() +
731 (*value - HIST_MIN_INPUT) *
732 gui->canvas->get_w() /
733 (HIST_MAX_INPUT - HIST_MIN_INPUT) -
738 float min = plugin->config.low_input[plugin->mode];
739 float max = plugin->config.high_input[plugin->mode];
740 float delta = (max - min) / 2.0;
741 float mid = min + delta;
742 float tmp = log10(1.0 / *value);
743 tmp = mid + delta * tmp;
745 //printf("HistogramCarrot::update %d %f %f\n", __LINE__, *value, tmp);
747 new_x = gui->canvas->get_x() -
749 (int)(gui->canvas->get_w() *
750 (tmp - HIST_MIN_INPUT) /
751 (HIST_MAX_INPUT - HIST_MIN_INPUT));
754 reposition_window(new_x, get_y());
757 int HistogramCarrot::button_press_event()
759 if(is_event_win() && get_buttonpress() == 1)
763 set_status(BC_Toggle::TOGGLE_DOWN);
765 BC_Toggle::update(0);
766 gui->active_value = get_value();
767 // Disable the other toggles
768 if(this != gui->low_input_carrot) gui->low_input_carrot->BC_Toggle::update(0);
769 if(this != gui->high_input_carrot) gui->high_input_carrot->BC_Toggle::update(0);
770 if(this != gui->gamma_carrot) gui->gamma_carrot->BC_Toggle::update(0);
771 if(this != gui->low_output_carrot) gui->low_output_carrot->BC_Toggle::update(0);
772 if(this != gui->high_output_carrot) gui->high_output_carrot->BC_Toggle::update(0);
773 starting_x = get_x();
774 offset_x = gui->get_relative_cursor_x();
775 offset_y = gui->get_relative_cursor_y();
776 //printf("HistogramCarrot::button_press_event %d %d %d\n", __LINE__, starting_x, offset_x);
784 int HistogramCarrot::button_release_event()
786 int result = BC_Toggle::button_release_event();
792 int HistogramCarrot::cursor_motion_event()
794 int cursor_x = gui->get_relative_cursor_x();
798 //printf("HistogramCarrot::cursor_motion_event %d %d\n", __LINE__, cursor_x);
799 int new_x = starting_x + cursor_x - offset_x;
803 float *value = get_value();
804 if(this == gui->gamma_carrot)
806 float min = gui->low_input_carrot->get_x();
807 float max = gui->high_input_carrot->get_x();
808 float delta = (max - min) / 2.0;
811 float mid = min + delta;
812 float tmp = (float)(new_x - mid) /
814 tmp = 1.0 / pow(10, tmp);
815 CLAMP(tmp, MIN_GAMMA, MAX_GAMMA);
817 //printf("HistogramCarrot::update %d %f\n", __LINE__, tmp);
822 int min_x = gui->canvas->get_x() - get_w() / 2;
823 int max_x = gui->canvas->get_x() + gui->canvas->get_w() - get_w() / 2;
824 CLAMP(new_x, min_x, max_x);
825 *value = HIST_MIN_INPUT +
826 (HIST_MAX_INPUT - HIST_MIN_INPUT) *
831 reposition_window(new_x, get_y());
835 (this == gui->low_input_carrot || this == gui->high_input_carrot),
838 plugin->send_configure_change();
852 HistogramSlider::HistogramSlider(HistogramMain *plugin,
853 HistogramWindow *gui,
859 : BC_SubWindow(x, y, w, h)
861 this->plugin = plugin;
863 this->is_input = is_input;
867 int HistogramSlider::input_to_pixel(float input)
869 return (int)((input - HIST_MIN_INPUT) / FLOAT_RANGE * get_w());
872 void HistogramSlider::update()
876 //int half_h = get_h() / 2;
877 //int quarter_h = get_h() / 4;
878 int mode = plugin->mode;
883 clear_box(0, 0, w, h);
890 case HISTOGRAM_GREEN:
898 for(int i = 0; i < w; i++)
900 int color = (int)(i * 0xff / w);
901 set_color(((r * color / 0xff) << 16) |
902 ((g * color / 0xff) << 8) |
905 draw_line(i, 0, i, h);
920 HistogramAuto::HistogramAuto(HistogramMain *plugin,
923 : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
925 this->plugin = plugin;
928 int HistogramAuto::handle_event()
930 plugin->config.automatic = get_value();
931 plugin->send_configure_change();
938 HistogramPlot::HistogramPlot(HistogramMain *plugin,
941 : BC_CheckBox(x, y, plugin->config.plot, _("Plot histogram"))
943 this->plugin = plugin;
946 int HistogramPlot::handle_event()
948 plugin->config.plot = get_value();
949 plugin->send_configure_change();
956 HistogramSplit::HistogramSplit(HistogramMain *plugin,
959 : BC_CheckBox(x, y, plugin->config.split, _("Split output"))
961 this->plugin = plugin;
964 int HistogramSplit::handle_event()
966 plugin->config.split = get_value();
967 plugin->send_configure_change();
972 HistogramMode::HistogramMode(HistogramMain *plugin,
977 : BC_Radial(x, y, plugin->mode == value, text)
979 this->plugin = plugin;
982 int HistogramMode::handle_event()
984 HistogramWindow *gui = (HistogramWindow*)plugin->thread->window;
985 plugin->mode = value;
986 plugin->current_point= -1;
987 gui->active_value = 0;
988 gui->low_input_carrot->BC_Toggle::update(0);
989 gui->gamma_carrot->BC_Toggle::update(0);
990 gui->high_input_carrot->BC_Toggle::update(0);
991 gui->low_output_carrot->BC_Toggle::update(0);
992 gui->high_output_carrot->BC_Toggle::update(0);
993 gui->update(1, 1, 1, 1);
994 // plugin->send_configure_change();
999 HistogramFrames::HistogramFrames(HistogramMain *plugin, HistogramWindow *gui,
1001 : BC_TumbleTextBox(gui, 0, 0, 65535, x, y, xS(80))
1003 this->plugin = plugin;
1007 int HistogramFrames::handle_event()
1009 plugin->config.frames = atoi(get_text());
1010 plugin->send_configure_change();
1014 void HistogramFrames::update(int frames)
1016 BC_TumbleTextBox::update((int64_t)frames);
1020 HistogramText::HistogramText(HistogramMain *plugin,
1021 HistogramWindow *gui, int x, int y, float hist_min, float hist_max)
1022 : BC_TumbleTextBox(gui, 0.0, hist_min, hist_max, x, y, xS(80))
1024 this->plugin = plugin;
1026 set_precision(DIGITS);
1027 set_increment(PRECISION);
1030 float* HistogramText::get_value()
1032 if( this == gui->low_input )
1033 return &plugin->config.low_input[plugin->mode];
1034 if( this == gui->high_input )
1035 return &plugin->config.high_input[plugin->mode];
1036 if( this == gui->gamma )
1037 return &plugin->config.gamma[plugin->mode];
1038 if( this == gui->low_output )
1039 return &plugin->config.low_output[plugin->mode];
1040 if( this == gui->high_output )
1041 return &plugin->config.high_output[plugin->mode];
1042 if( this == gui->threshold )
1043 return &plugin->config.threshold;
1047 int HistogramText::handle_event()
1049 float *output = get_value();
1051 *output = atof(get_text());
1053 gui->update(1, 1, 0, 0);
1054 plugin->send_configure_change();
1058 void HistogramText::update()
1060 float *output = get_value();
1062 BC_TumbleTextBox::update(*output);