4 * Copyright (C) 2008 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"
24 #include "bistogram.h"
25 #include "bistogramconfig.h"
26 #include "bistogramwindow.h"
34 HistogramWindow::HistogramWindow(HistogramMain *plugin)
35 : PluginClientWindow(plugin, 440, 480, 440, 480, 0)
37 this->plugin = plugin;
43 HistogramWindow::~HistogramWindow()
50 #include "min_picon_png.h"
51 #include "mid_picon_png.h"
52 #include "max_picon_png.h"
53 static VFramePng max_picon_image(max_picon_png);
54 static VFramePng mid_picon_image(mid_picon_png);
55 static VFramePng min_picon_image(min_picon_png);
57 void HistogramWindow::create_objects()
59 int x = 10, y = 10, x1 = 10;
62 max_picon = create_pixmap(&max_picon_image);
63 mid_picon = create_pixmap(&mid_picon_image);
64 min_picon = create_pixmap(&min_picon_image);
65 add_subwindow(mode_v = new HistogramMode(plugin,
71 add_subwindow(mode_r = new HistogramMode(plugin,
77 add_subwindow(mode_g = new HistogramMode(plugin,
83 add_subwindow(mode_b = new HistogramMode(plugin,
89 // add_subwindow(mode_a = new HistogramMode(plugin,
97 add_subwindow(title = new BC_Title(x, y, _("Input X:")));
98 x += title->get_w() + 10;
99 input_x = new HistogramInputText(plugin,
104 input_x->create_objects();
106 x += input_x->get_w() + 10;
107 add_subwindow(title = new BC_Title(x, y, _("Input Y:")));
108 x += title->get_w() + 10;
109 input_y = new HistogramInputText(plugin,
114 input_y->create_objects();
119 canvas_w = get_w() - x - x;
120 canvas_h = get_h() - y - 170;
122 title2_x = x + (int)(canvas_w * -HIST_MIN_INPUT / FLOAT_RANGE);
123 title3_x = x + (int)(canvas_w * (1.0 - HIST_MIN_INPUT) / FLOAT_RANGE);
124 title4_x = x + (int)(canvas_w);
125 add_subwindow(canvas = new HistogramCanvas(plugin,
131 draw_canvas_overlay();
134 y += canvas->get_h() + 1;
135 add_subwindow(new BC_Title(title1_x,
138 add_subwindow(new BC_Title(title2_x,
141 add_subwindow(new BC_Title(title3_x - get_text_width(MEDIUMFONT, "100"),
144 add_subwindow(new BC_Title(title4_x - get_text_width(MEDIUMFONT, "110"),
149 add_subwindow(title = new BC_Title(x, y, _("Output min:")));
150 x += title->get_w() + 10;
151 output_min = new HistogramOutputText(plugin,
155 &plugin->config.output_min[plugin->mode]);
156 output_min->create_objects();
157 x += output_min->get_w() + 10;
158 add_subwindow(new BC_Title(x, y, _("Output Max:")));
159 x += title->get_w() + 10;
160 output_max = new HistogramOutputText(plugin,
164 &plugin->config.output_max[plugin->mode]);
165 output_max->create_objects();
170 add_subwindow(output = new HistogramSlider(plugin,
181 add_subwindow(automatic = new HistogramAuto(plugin,
186 add_subwindow(new HistogramReset(plugin,
190 add_subwindow(new BC_Title(x, y, _("Threshold:")));
192 threshold = new HistogramOutputText(plugin,
196 &plugin->config.threshold);
197 threshold->create_objects();
200 add_subwindow(split = new HistogramSplit(plugin, x, y));
203 add_subwindow(new BC_Title(x,y, _("Interpolation:")));
205 add_subwindow(smoothModeChoser = new HistogramSmoothMode(plugin, this, x, y));
206 smoothModeChoser->create_objects();
211 int HistogramWindow::keypress_event()
214 if(get_keypress() == BACKSPACE ||
215 get_keypress() == DELETE)
217 if(plugin->current_point >= 0)
219 HistogramPoint *current =
220 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
222 plugin->current_point = -1;
225 plugin->send_configure_change();
232 void HistogramWindow::update(int do_input)
234 automatic->update(plugin->config.automatic);
235 threshold->update(plugin->config.threshold);
238 if(do_input) update_input();
242 void HistogramWindow::update_input()
248 void HistogramWindow::update_output()
251 output_min->update(plugin->config.output_min[plugin->mode]);
252 output_max->update(plugin->config.output_max[plugin->mode]);
255 void HistogramWindow::update_mode()
257 mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
258 mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
259 mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
260 mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
261 output_min->output = &plugin->config.output_min[plugin->mode];
262 output_max->output = &plugin->config.output_max[plugin->mode];
265 void HistogramWindow::draw_canvas_overlay()
269 // Calculate output curve
270 plugin->tabulate_curve(plugin->mode, 0);
273 canvas->set_color(0xffff00);
274 for(int i = 0; i < canvas_w; i++)
276 float input = (float)i /
280 float output = plugin->calculate_smooth(input, plugin->mode);
282 int y2 = canvas_h - (int)(output * canvas_h);
285 canvas->draw_line(i - 1, y1, i, y2);
291 // Draw output points
292 HistogramPoint *current = plugin->config.points[plugin->mode].first;
296 canvas->set_color(0x00ff00);
297 int x = (int)((current->x - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
298 int y = (int)(canvas_h - current->y * canvas_h);
299 if(number == plugin->current_point)
300 canvas->draw_box(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
302 canvas->draw_rectangle(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
305 canvas->draw_line(x0, y0, x, y);
308 if (plugin->config.smoothMode > HISTOGRAM_LINEAR) {
310 canvas->set_color(0x0000ff);
311 x2 = (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
312 x1 = (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
313 y2 = (int)(canvas_h - (current->y + current->xoffset_right * current->gradient) * canvas_h);
314 y1 = (int)(canvas_h - (current->y + current->xoffset_left * current->gradient) * canvas_h);
315 /* x2 = x + (title3_x - title2_x)/20;
316 x1 = x - (title3_x - title2_x)/20;
317 y1 = y + (int)(current->gradient * (float)(canvas_h)/20.0);
318 y2 = y - (int)(current->gradient * (float)(canvas_h)/20.0);
319 // int y2 = (int)(canvas_h - canvas_h * (current->y + current->gradient /10));*/
320 canvas->draw_line(x1,y1,x2,y2);
322 canvas->draw_circle(x1 - BOX_SIZE / 4, y1 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
323 canvas->draw_circle(x2 - BOX_SIZE / 4, y2 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
331 // Draw 0 and 100% lines.
332 canvas->set_color(0xff0000);
333 canvas->draw_line(title2_x - canvas->get_x(),
335 title2_x - canvas->get_x(),
337 canvas->draw_line(title3_x - canvas->get_x(),
339 title3_x - canvas->get_x(),
343 void HistogramWindow::update_canvas()
345 int *accum = plugin->accum[plugin->mode];
346 int accum_per_canvas_i = HISTOGRAM_SLOTS / canvas_w + 1;
347 float accum_per_canvas_f = (float)HISTOGRAM_SLOTS / canvas_w;
351 for(int i = 0; i < HISTOGRAM_SLOTS; i++)
353 if(accum && accum[i] > normalize) normalize = accum[i];
359 for(int i = 0; i < canvas_w; i++)
361 int accum_start = (int)(accum_per_canvas_f * i);
362 int accum_end = accum_start + accum_per_canvas_i;
364 for(int j = accum_start; j < accum_end; j++)
366 max = MAX(accum[j], max);
369 // max = max * canvas_h / normalize;
370 max = (int)(log(max) / log(normalize) * canvas_h);
372 canvas->set_color(0xffffff);
373 canvas->draw_line(i, 0, i, canvas_h - max);
374 canvas->set_color(0x000000);
375 canvas->draw_line(i, canvas_h - max, i, canvas_h);
380 canvas->set_color(0xffffff);
381 canvas->draw_box(0, 0, canvas_w, canvas_h);
385 draw_canvas_overlay();
396 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
397 HistogramWindow *gui,
408 this->plugin = plugin;
412 int HistogramCanvas::button_press_event()
415 if(is_event_win() && cursor_inside())
417 if(!plugin->dragging_point)
419 HistogramPoint *new_point = 0;
421 // Search for existing point under cursor
422 HistogramPoint *current = plugin->config.points[plugin->mode].first;
423 plugin->current_point = -1;
427 int x = (int)((current->x - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
428 int y = (int)(gui->canvas_h - current->y * gui->canvas_h);
430 /* Check for click on main point */
431 if(get_cursor_x() >= x - BOX_SIZE / 2 &&
432 get_cursor_y() >= y - BOX_SIZE / 2 &&
433 get_cursor_x() < x + BOX_SIZE / 2 &&
434 get_cursor_y() < y + BOX_SIZE / 2)
436 plugin->current_point =
437 plugin->config.points[plugin->mode].number_of(current);
438 plugin->point_x_offset = get_cursor_x() - x;
439 plugin->point_y_offset = get_cursor_y() - y;
443 // if (plugin->config.smoothMode == HISTOGRAM_LINEAR)
447 (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
449 (int)(gui->canvas_h - (current->y + current->xoffset_right * current->gradient) *
452 /* Check for click on right handle */
453 if(get_cursor_x() >= xright - BOX_SIZE / 2 &&
454 get_cursor_y() >= yright - BOX_SIZE / 2 &&
455 get_cursor_x() < xright + BOX_SIZE / 2 &&
456 get_cursor_y() < yright + BOX_SIZE / 2)
458 plugin->current_point =
459 plugin->config.points[plugin->mode].number_of(current);
460 plugin->point_x_offset = get_cursor_x() - xright;
461 plugin->point_y_offset = get_cursor_y() - yright;
466 /* Check for click on left handle */
468 (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
470 (int)(gui->canvas_h - (current->y + current->xoffset_left * current->gradient) *
472 if(get_cursor_x() >= xleft - BOX_SIZE / 2 &&
473 get_cursor_y() >= yleft - BOX_SIZE / 2 &&
474 get_cursor_x() < xleft + BOX_SIZE / 2 &&
475 get_cursor_y() < yleft + BOX_SIZE / 2)
477 plugin->current_point =
478 plugin->config.points[plugin->mode].number_of(current);
479 plugin->point_x_offset = get_cursor_x() - xleft;
480 plugin->point_y_offset = get_cursor_y() - yleft;
489 if(plugin->current_point < 0)
491 // Create new point under cursor
492 float current_x = (float)get_cursor_x() *
496 float current_y = 1.0 -
497 (float)get_cursor_y() /
500 plugin->config.points[plugin->mode].insert(current_x, current_y);
501 plugin->current_point =
502 plugin->config.points[plugin->mode].number_of(new_point);
503 plugin->point_x_offset = 0;
504 plugin->point_y_offset = 0;
507 // Get 2 points surrounding current position
508 float x1 = 0, x2 = 1, y1 = 0, y2 = 1;
510 HistogramPoint *current = plugin->config.points[plugin->mode].first;
512 while(current && !done)
514 if(current->x > current_x)
524 current = plugin->config.points[plugin->mode].last;
526 while(current && !done)
528 if(current->x <= current_x)
537 new_point->gradient = (y2 - y1) / (x2 - x1);
543 plugin->dragging_point = dragID;
546 plugin->config.boundaries();
548 gui->update_canvas();
551 plugin->send_configure_change();
558 int HistogramCanvas::cursor_motion_event()
560 if(plugin->dragging_point)
562 HistogramPoint * current_point = plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
565 (float)(get_cursor_x() - plugin->point_x_offset) *
569 float current_y = 1.0 -
570 (float)(get_cursor_y() - plugin->point_y_offset) /
573 switch(plugin->dragging_point)
575 case 1: /* Main point dragged */
576 current_point->x = current_x;
577 current_point->y = current_y;
579 case 2: /* Right control point dragged */
580 if (current_x - current_point->x > 0)
582 current_point->xoffset_right = current_x - current_point->x;
583 current_point->gradient = (current_y - current_point->y) / (current_x - current_point->x);
586 case 3: /* Left control point dragged */
587 if (current_x - current_point->x < 0)
589 current_point->xoffset_left = current_x - current_point->x;
590 current_point->gradient = (current_point->y - current_y) / (current_point->x - current_x);
595 plugin->config.boundaries();
597 gui->update_canvas();
598 plugin->send_configure_change();
604 int HistogramCanvas::button_release_event()
606 if(plugin->dragging_point)
608 // Test for out of order points to delete.
609 HistogramPoint *current =
610 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
611 HistogramPoint *prev = PREVIOUS;
612 HistogramPoint *next = NEXT;
614 if((prev && prev->x >= current->x) ||
615 (next && next->x <= current->x))
618 plugin->current_point = -1;
619 plugin->config.boundaries();
621 gui->update_canvas();
622 plugin->send_configure_change();
625 plugin->dragging_point = 0;
636 HistogramReset::HistogramReset(HistogramMain *plugin,
639 : BC_GenericButton(x, y, _("Reset"))
641 this->plugin = plugin;
643 int HistogramReset::handle_event()
645 plugin->config.reset(0);
646 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
648 window->update_canvas();
649 plugin->send_configure_change();
661 HistogramSlider::HistogramSlider(HistogramMain *plugin,
662 HistogramWindow *gui,
668 : BC_SubWindow(x, y, w, h)
670 this->plugin = plugin;
672 this->is_input = is_input;
676 int HistogramSlider::input_to_pixel(float input)
678 return (int)((input - HIST_MIN_INPUT) / FLOAT_RANGE * get_w());
681 int HistogramSlider::button_press_event()
683 if(is_event_win() && cursor_inside())
687 int half_h = get_h() / 2;
691 if(operation == NONE)
693 int x1 = input_to_pixel(plugin->config.output_min[plugin->mode]) -
694 gui->mid_picon->get_w() / 2;
695 int x2 = x1 + gui->mid_picon->get_w();
696 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
697 get_cursor_y() >= half_h && get_cursor_y() < h)
699 operation = DRAG_MIN_OUTPUT;
703 if(operation == NONE)
705 int x1 = input_to_pixel(plugin->config.output_max[plugin->mode]) -
706 gui->mid_picon->get_w() / 2;
707 int x2 = x1 + gui->mid_picon->get_w();
708 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
709 get_cursor_y() >= half_h && get_cursor_y() < h)
711 operation = DRAG_MAX_OUTPUT;
719 int HistogramSlider::button_release_event()
721 if(operation != NONE)
729 int HistogramSlider::cursor_motion_event()
731 if(operation != NONE)
733 float value = (float)get_cursor_x() / get_w() * FLOAT_RANGE + HIST_MIN_INPUT;
734 CLAMP(value, HIST_MIN_INPUT, HIST_MAX_INPUT);
738 case DRAG_MIN_OUTPUT:
739 value = MIN(plugin->config.output_max[plugin->mode], value);
740 plugin->config.output_min[plugin->mode] = value;
742 case DRAG_MAX_OUTPUT:
743 value = MAX(plugin->config.output_min[plugin->mode], value);
744 plugin->config.output_max[plugin->mode] = value;
748 plugin->config.boundaries();
749 gui->update_output();
751 plugin->send_configure_change();
757 void HistogramSlider::update()
761 int half_h = get_h() / 2;
762 // int quarter_h = get_h() / 4;
763 int mode = plugin->mode;
768 clear_box(0, 0, w, h);
775 case HISTOGRAM_GREEN:
783 for(int i = 0; i < w; i++)
785 int color = (int)(i * 0xff / w);
786 set_color(((r * color / 0xff) << 16) |
787 ((g * color / 0xff) << 8) |
790 draw_line(i, 0, i, half_h);
795 min = plugin->config.output_min[plugin->mode];
796 max = plugin->config.output_max[plugin->mode];
798 draw_pixmap(gui->min_picon,
799 input_to_pixel(min) - gui->min_picon->get_w() / 2,
801 draw_pixmap(gui->max_picon,
802 input_to_pixel(max) - gui->max_picon->get_w() / 2,
817 HistogramAuto::HistogramAuto(HistogramMain *plugin,
820 : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
822 this->plugin = plugin;
825 int HistogramAuto::handle_event()
827 plugin->config.automatic = get_value();
828 plugin->send_configure_change();
835 HistogramSplit::HistogramSplit(HistogramMain *plugin,
838 : BC_CheckBox(x, y, plugin->config.split, _("Split picture"))
840 this->plugin = plugin;
843 int HistogramSplit::handle_event()
845 plugin->config.split = get_value();
846 plugin->send_configure_change();
853 HistogramMode::HistogramMode(HistogramMain *plugin,
858 : BC_Radial(x, y, plugin->mode == value, text)
860 this->plugin = plugin;
863 int HistogramMode::handle_event()
865 plugin->mode = value;
866 plugin->current_point= -1;
867 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
868 window->update_canvas();
869 window->update_mode();
870 window->update_input();
871 window->update_canvas();
872 window->update_output();
873 window->output->update();
874 // plugin->send_configure_change();
886 HistogramOutputText::HistogramOutputText(HistogramMain *plugin,
887 HistogramWindow *gui,
891 : BC_TumbleTextBox(gui,
892 output ? (float)*output : 0.0,
893 (float)HIST_MIN_INPUT,
894 (float)HIST_MAX_INPUT,
899 this->plugin = plugin;
900 this->output = output;
901 set_precision(DIGITS);
902 set_increment(PRECISION);
906 int HistogramOutputText::handle_event()
910 *output = atof(get_text());
913 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
914 window->output->update();
915 plugin->send_configure_change();
926 HistogramInputText::HistogramInputText(HistogramMain *plugin,
927 HistogramWindow *gui,
931 : BC_TumbleTextBox(gui,
933 (float)HIST_MIN_INPUT,
934 (float)HIST_MAX_INPUT,
940 this->plugin = plugin;
942 set_precision(DIGITS);
943 set_increment(PRECISION);
947 int HistogramInputText::handle_event()
949 if(plugin->current_point >= 0 &&
950 plugin->current_point < plugin->config.points[plugin->mode].total())
952 HistogramPoint *point =
953 plugin->config.points[plugin->mode].get_item_number(
954 plugin->current_point);
959 point->x = atof(get_text());
961 point->y = atof(get_text());
963 plugin->config.boundaries();
964 gui->update_canvas();
966 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
967 window->output->update();
968 plugin->send_configure_change();
974 void HistogramInputText::update()
976 if(plugin->current_point >= 0 &&
977 plugin->current_point < plugin->config.points[plugin->mode].total())
979 HistogramPoint *point =
981 plugin->config.points[plugin->mode].get_item_number(
982 plugin->current_point);
987 BC_TumbleTextBox::update(point->x);
989 BC_TumbleTextBox::update(point->y);
993 BC_TumbleTextBox::update((float)0.0);
998 BC_TumbleTextBox::update((float)0.0);
1004 HistogramSmoothMode::HistogramSmoothMode(HistogramMain*plugin,
1005 HistogramWindow *gui,
1008 : BC_PopupMenu(x, y, 120, to_text(plugin->config.smoothMode), 1)
1010 this->plugin = plugin;
1013 void HistogramSmoothMode::create_objects()
1015 add_item(new BC_MenuItem(to_text(HISTOGRAM_LINEAR)));
1016 add_item(new BC_MenuItem(to_text(HISTOGRAM_POLYNOMINAL)));
1017 add_item(new BC_MenuItem(to_text(HISTOGRAM_BEZIER)));
1020 char* HistogramSmoothMode::to_text(int mode)
1024 case HISTOGRAM_LINEAR:
1026 case HISTOGRAM_POLYNOMINAL:
1027 return _("Polynominal");
1028 case HISTOGRAM_BEZIER:
1034 int HistogramSmoothMode::from_text(char *text)
1036 if(!strcmp(text, to_text(HISTOGRAM_LINEAR)))
1037 return HISTOGRAM_LINEAR;
1038 if(!strcmp(text, to_text(HISTOGRAM_POLYNOMINAL)))
1039 return HISTOGRAM_POLYNOMINAL;
1040 if(!strcmp(text, to_text(HISTOGRAM_BEZIER)))
1041 return HISTOGRAM_BEZIER;
1042 return HISTOGRAM_LINEAR;
1045 int HistogramSmoothMode::handle_event()
1047 plugin->config.smoothMode = from_text(get_text());
1048 gui->update_canvas();
1049 plugin->send_configure_change();