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;
40 HistogramWindow::~HistogramWindow()
44 #include "min_picon_png.h"
45 #include "mid_picon_png.h"
46 #include "max_picon_png.h"
47 static VFramePng max_picon_image(max_picon_png);
48 static VFramePng mid_picon_image(mid_picon_png);
49 static VFramePng min_picon_image(min_picon_png);
51 void HistogramWindow::create_objects()
53 int x = 10, y = 10, x1 = 10;
56 max_picon = create_pixmap(&max_picon_image);
57 mid_picon = create_pixmap(&mid_picon_image);
58 min_picon = create_pixmap(&min_picon_image);
59 add_subwindow(mode_v = new HistogramMode(plugin,
65 add_subwindow(mode_r = new HistogramMode(plugin,
71 add_subwindow(mode_g = new HistogramMode(plugin,
77 add_subwindow(mode_b = new HistogramMode(plugin,
83 // add_subwindow(mode_a = new HistogramMode(plugin,
91 add_subwindow(title = new BC_Title(x, y, _("Input X:")));
92 x += title->get_w() + 10;
93 input_x = new HistogramInputText(plugin,
98 input_x->create_objects();
100 x += input_x->get_w() + 10;
101 add_subwindow(title = new BC_Title(x, y, _("Input Y:")));
102 x += title->get_w() + 10;
103 input_y = new HistogramInputText(plugin,
108 input_y->create_objects();
113 canvas_w = get_w() - x - x;
114 canvas_h = get_h() - y - 170;
116 title2_x = x + (int)(canvas_w * -HIST_MIN_INPUT / FLOAT_RANGE);
117 title3_x = x + (int)(canvas_w * (1.0 - HIST_MIN_INPUT) / FLOAT_RANGE);
118 title4_x = x + (int)(canvas_w);
119 add_subwindow(canvas = new HistogramCanvas(plugin,
125 draw_canvas_overlay();
128 y += canvas->get_h() + 1;
129 add_subwindow(new BC_Title(title1_x,
132 add_subwindow(new BC_Title(title2_x,
135 add_subwindow(new BC_Title(title3_x - get_text_width(MEDIUMFONT, "100"),
138 add_subwindow(new BC_Title(title4_x - get_text_width(MEDIUMFONT, "110"),
143 add_subwindow(title = new BC_Title(x, y, _("Output min:")));
144 x += title->get_w() + 10;
145 output_min = new HistogramOutputText(plugin,
149 &plugin->config.output_min[plugin->mode]);
150 output_min->create_objects();
151 x += output_min->get_w() + 10;
152 add_subwindow(new BC_Title(x, y, _("Output Max:")));
153 x += title->get_w() + 10;
154 output_max = new HistogramOutputText(plugin,
158 &plugin->config.output_max[plugin->mode]);
159 output_max->create_objects();
164 add_subwindow(output = new HistogramSlider(plugin,
175 add_subwindow(automatic = new HistogramAuto(plugin,
180 add_subwindow(new HistogramReset(plugin,
184 add_subwindow(new BC_Title(x, y, _("Threshold:")));
186 threshold = new HistogramOutputText(plugin,
190 &plugin->config.threshold);
191 threshold->create_objects();
194 add_subwindow(split = new HistogramSplit(plugin, x, y));
197 add_subwindow(new BC_Title(x,y, _("Interpolation:")));
199 add_subwindow(smoothModeChoser = new HistogramSmoothMode(plugin, this, x, y));
200 smoothModeChoser->create_objects();
205 int HistogramWindow::keypress_event()
208 if(get_keypress() == BACKSPACE ||
209 get_keypress() == DELETE)
211 if(plugin->current_point >= 0)
213 HistogramPoint *current =
214 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
216 plugin->current_point = -1;
219 plugin->send_configure_change();
226 void HistogramWindow::update(int do_input)
228 automatic->update(plugin->config.automatic);
229 threshold->update(plugin->config.threshold);
232 if(do_input) update_input();
236 void HistogramWindow::update_input()
242 void HistogramWindow::update_output()
245 output_min->update(plugin->config.output_min[plugin->mode]);
246 output_max->update(plugin->config.output_max[plugin->mode]);
249 void HistogramWindow::update_mode()
251 mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
252 mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
253 mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
254 mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
255 output_min->output = &plugin->config.output_min[plugin->mode];
256 output_max->output = &plugin->config.output_max[plugin->mode];
259 void HistogramWindow::draw_canvas_overlay()
263 // Calculate output curve
264 plugin->tabulate_curve(plugin->mode, 0);
267 canvas->set_color(0xffff00);
268 for(int i = 0; i < canvas_w; i++)
270 float input = (float)i /
274 float output = plugin->calculate_smooth(input, plugin->mode);
276 int y2 = canvas_h - (int)(output * canvas_h);
279 canvas->draw_line(i - 1, y1, i, y2);
285 // Draw output points
286 HistogramPoint *current = plugin->config.points[plugin->mode].first;
290 canvas->set_color(0x00ff00);
291 int x = (int)((current->x - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
292 int y = (int)(canvas_h - current->y * canvas_h);
293 if(number == plugin->current_point)
294 canvas->draw_box(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
296 canvas->draw_rectangle(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
299 canvas->draw_line(x0, y0, x, y);
302 if (plugin->config.smoothMode > HISTOGRAM_LINEAR) {
304 canvas->set_color(0x0000ff);
305 x2 = (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
306 x1 = (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
307 y2 = (int)(canvas_h - (current->y + current->xoffset_right * current->gradient) * canvas_h);
308 y1 = (int)(canvas_h - (current->y + current->xoffset_left * current->gradient) * canvas_h);
309 /* x2 = x + (title3_x - title2_x)/20;
310 x1 = x - (title3_x - title2_x)/20;
311 y1 = y + (int)(current->gradient * (float)(canvas_h)/20.0);
312 y2 = y - (int)(current->gradient * (float)(canvas_h)/20.0);
313 // int y2 = (int)(canvas_h - canvas_h * (current->y + current->gradient /10));*/
314 canvas->draw_line(x1,y1,x2,y2);
316 canvas->draw_circle(x1 - BOX_SIZE / 4, y1 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
317 canvas->draw_circle(x2 - BOX_SIZE / 4, y2 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
325 // Draw 0 and 100% lines.
326 canvas->set_color(0xff0000);
327 canvas->draw_line(title2_x - canvas->get_x(),
329 title2_x - canvas->get_x(),
331 canvas->draw_line(title3_x - canvas->get_x(),
333 title3_x - canvas->get_x(),
337 void HistogramWindow::update_canvas()
339 int *accum = plugin->accum[plugin->mode];
340 int accum_per_canvas_i = HISTOGRAM_SLOTS / canvas_w + 1;
341 float accum_per_canvas_f = (float)HISTOGRAM_SLOTS / canvas_w;
345 for(int i = 0; i < HISTOGRAM_SLOTS; i++)
347 if(accum && accum[i] > normalize) normalize = accum[i];
353 for(int i = 0; i < canvas_w; i++)
355 int accum_start = (int)(accum_per_canvas_f * i);
356 int accum_end = accum_start + accum_per_canvas_i;
358 for(int j = accum_start; j < accum_end; j++)
360 max = MAX(accum[j], max);
363 // max = max * canvas_h / normalize;
364 max = (int)(log(max) / log(normalize) * canvas_h);
366 canvas->set_color(0xffffff);
367 canvas->draw_line(i, 0, i, canvas_h - max);
368 canvas->set_color(0x000000);
369 canvas->draw_line(i, canvas_h - max, i, canvas_h);
374 canvas->set_color(0xffffff);
375 canvas->draw_box(0, 0, canvas_w, canvas_h);
379 draw_canvas_overlay();
390 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
391 HistogramWindow *gui,
402 this->plugin = plugin;
406 int HistogramCanvas::button_press_event()
409 if(is_event_win() && cursor_inside())
411 if(!plugin->dragging_point)
413 HistogramPoint *new_point = 0;
415 // Search for existing point under cursor
416 HistogramPoint *current = plugin->config.points[plugin->mode].first;
417 plugin->current_point = -1;
421 int x = (int)((current->x - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
422 int y = (int)(gui->canvas_h - current->y * gui->canvas_h);
424 /* Check for click on main point */
425 if(get_cursor_x() >= x - BOX_SIZE / 2 &&
426 get_cursor_y() >= y - BOX_SIZE / 2 &&
427 get_cursor_x() < x + BOX_SIZE / 2 &&
428 get_cursor_y() < y + BOX_SIZE / 2)
430 plugin->current_point =
431 plugin->config.points[plugin->mode].number_of(current);
432 plugin->point_x_offset = get_cursor_x() - x;
433 plugin->point_y_offset = get_cursor_y() - y;
437 // if (plugin->config.smoothMode == HISTOGRAM_LINEAR)
441 (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
443 (int)(gui->canvas_h - (current->y + current->xoffset_right * current->gradient) *
446 /* Check for click on right handle */
447 if(get_cursor_x() >= xright - BOX_SIZE / 2 &&
448 get_cursor_y() >= yright - BOX_SIZE / 2 &&
449 get_cursor_x() < xright + BOX_SIZE / 2 &&
450 get_cursor_y() < yright + BOX_SIZE / 2)
452 plugin->current_point =
453 plugin->config.points[plugin->mode].number_of(current);
454 plugin->point_x_offset = get_cursor_x() - xright;
455 plugin->point_y_offset = get_cursor_y() - yright;
460 /* Check for click on left handle */
462 (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
464 (int)(gui->canvas_h - (current->y + current->xoffset_left * current->gradient) *
466 if(get_cursor_x() >= xleft - BOX_SIZE / 2 &&
467 get_cursor_y() >= yleft - BOX_SIZE / 2 &&
468 get_cursor_x() < xleft + BOX_SIZE / 2 &&
469 get_cursor_y() < yleft + BOX_SIZE / 2)
471 plugin->current_point =
472 plugin->config.points[plugin->mode].number_of(current);
473 plugin->point_x_offset = get_cursor_x() - xleft;
474 plugin->point_y_offset = get_cursor_y() - yleft;
483 if(plugin->current_point < 0)
485 // Create new point under cursor
486 float current_x = (float)get_cursor_x() *
490 float current_y = 1.0 -
491 (float)get_cursor_y() /
494 plugin->config.points[plugin->mode].insert(current_x, current_y);
495 plugin->current_point =
496 plugin->config.points[plugin->mode].number_of(new_point);
497 plugin->point_x_offset = 0;
498 plugin->point_y_offset = 0;
501 // Get 2 points surrounding current position
502 float x1 = 0, x2 = 1, y1 = 0, y2 = 1;
504 HistogramPoint *current = plugin->config.points[plugin->mode].first;
506 while(current && !done)
508 if(current->x > current_x)
518 current = plugin->config.points[plugin->mode].last;
520 while(current && !done)
522 if(current->x <= current_x)
531 new_point->gradient = (y2 - y1) / (x2 - x1);
537 plugin->dragging_point = dragID;
540 plugin->config.boundaries();
542 gui->update_canvas();
545 plugin->send_configure_change();
552 int HistogramCanvas::cursor_motion_event()
554 if(plugin->dragging_point)
556 HistogramPoint * current_point = plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
559 (float)(get_cursor_x() - plugin->point_x_offset) *
563 float current_y = 1.0 -
564 (float)(get_cursor_y() - plugin->point_y_offset) /
567 switch(plugin->dragging_point)
569 case 1: /* Main point dragged */
570 current_point->x = current_x;
571 current_point->y = current_y;
573 case 2: /* Right control point dragged */
574 if (current_x - current_point->x > 0)
576 current_point->xoffset_right = current_x - current_point->x;
577 current_point->gradient = (current_y - current_point->y) / (current_x - current_point->x);
580 case 3: /* Left control point dragged */
581 if (current_x - current_point->x < 0)
583 current_point->xoffset_left = current_x - current_point->x;
584 current_point->gradient = (current_point->y - current_y) / (current_point->x - current_x);
589 plugin->config.boundaries();
591 gui->update_canvas();
592 plugin->send_configure_change();
598 int HistogramCanvas::button_release_event()
600 if(plugin->dragging_point)
602 // Test for out of order points to delete.
603 HistogramPoint *current =
604 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
605 HistogramPoint *prev = PREVIOUS;
606 HistogramPoint *next = NEXT;
608 if((prev && prev->x >= current->x) ||
609 (next && next->x <= current->x))
612 plugin->current_point = -1;
613 plugin->config.boundaries();
615 gui->update_canvas();
616 plugin->send_configure_change();
619 plugin->dragging_point = 0;
630 HistogramReset::HistogramReset(HistogramMain *plugin,
633 : BC_GenericButton(x, y, _("Reset"))
635 this->plugin = plugin;
637 int HistogramReset::handle_event()
639 plugin->config.reset(0);
640 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
642 window->update_canvas();
643 plugin->send_configure_change();
655 HistogramSlider::HistogramSlider(HistogramMain *plugin,
656 HistogramWindow *gui,
662 : BC_SubWindow(x, y, w, h)
664 this->plugin = plugin;
666 this->is_input = is_input;
670 int HistogramSlider::input_to_pixel(float input)
672 return (int)((input - HIST_MIN_INPUT) / FLOAT_RANGE * get_w());
675 int HistogramSlider::button_press_event()
677 if(is_event_win() && cursor_inside())
681 int half_h = get_h() / 2;
685 if(operation == NONE)
687 int x1 = input_to_pixel(plugin->config.output_min[plugin->mode]) -
688 gui->mid_picon->get_w() / 2;
689 int x2 = x1 + gui->mid_picon->get_w();
690 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
691 get_cursor_y() >= half_h && get_cursor_y() < h)
693 operation = DRAG_MIN_OUTPUT;
697 if(operation == NONE)
699 int x1 = input_to_pixel(plugin->config.output_max[plugin->mode]) -
700 gui->mid_picon->get_w() / 2;
701 int x2 = x1 + gui->mid_picon->get_w();
702 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
703 get_cursor_y() >= half_h && get_cursor_y() < h)
705 operation = DRAG_MAX_OUTPUT;
713 int HistogramSlider::button_release_event()
715 if(operation != NONE)
723 int HistogramSlider::cursor_motion_event()
725 if(operation != NONE)
727 float value = (float)get_cursor_x() / get_w() * FLOAT_RANGE + HIST_MIN_INPUT;
728 CLAMP(value, HIST_MIN_INPUT, HIST_MAX_INPUT);
732 case DRAG_MIN_OUTPUT:
733 value = MIN(plugin->config.output_max[plugin->mode], value);
734 plugin->config.output_min[plugin->mode] = value;
736 case DRAG_MAX_OUTPUT:
737 value = MAX(plugin->config.output_min[plugin->mode], value);
738 plugin->config.output_max[plugin->mode] = value;
742 plugin->config.boundaries();
743 gui->update_output();
745 plugin->send_configure_change();
751 void HistogramSlider::update()
755 int half_h = get_h() / 2;
756 // int quarter_h = get_h() / 4;
757 int mode = plugin->mode;
762 clear_box(0, 0, w, h);
769 case HISTOGRAM_GREEN:
777 for(int i = 0; i < w; i++)
779 int color = (int)(i * 0xff / w);
780 set_color(((r * color / 0xff) << 16) |
781 ((g * color / 0xff) << 8) |
784 draw_line(i, 0, i, half_h);
789 min = plugin->config.output_min[plugin->mode];
790 max = plugin->config.output_max[plugin->mode];
792 draw_pixmap(gui->min_picon,
793 input_to_pixel(min) - gui->min_picon->get_w() / 2,
795 draw_pixmap(gui->max_picon,
796 input_to_pixel(max) - gui->max_picon->get_w() / 2,
811 HistogramAuto::HistogramAuto(HistogramMain *plugin,
814 : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
816 this->plugin = plugin;
819 int HistogramAuto::handle_event()
821 plugin->config.automatic = get_value();
822 plugin->send_configure_change();
829 HistogramSplit::HistogramSplit(HistogramMain *plugin,
832 : BC_CheckBox(x, y, plugin->config.split, _("Split picture"))
834 this->plugin = plugin;
837 int HistogramSplit::handle_event()
839 plugin->config.split = get_value();
840 plugin->send_configure_change();
847 HistogramMode::HistogramMode(HistogramMain *plugin,
852 : BC_Radial(x, y, plugin->mode == value, text)
854 this->plugin = plugin;
857 int HistogramMode::handle_event()
859 plugin->mode = value;
860 plugin->current_point= -1;
861 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
862 window->update_canvas();
863 window->update_mode();
864 window->update_input();
865 window->update_canvas();
866 window->update_output();
867 window->output->update();
868 // plugin->send_configure_change();
880 HistogramOutputText::HistogramOutputText(HistogramMain *plugin,
881 HistogramWindow *gui,
885 : BC_TumbleTextBox(gui,
886 output ? (float)*output : 0.0,
887 (float)HIST_MIN_INPUT,
888 (float)HIST_MAX_INPUT,
893 this->plugin = plugin;
894 this->output = output;
895 set_precision(DIGITS);
896 set_increment(PRECISION);
900 int HistogramOutputText::handle_event()
904 *output = atof(get_text());
907 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
908 window->output->update();
909 plugin->send_configure_change();
920 HistogramInputText::HistogramInputText(HistogramMain *plugin,
921 HistogramWindow *gui,
925 : BC_TumbleTextBox(gui,
927 (float)HIST_MIN_INPUT,
928 (float)HIST_MAX_INPUT,
934 this->plugin = plugin;
936 set_precision(DIGITS);
937 set_increment(PRECISION);
941 int HistogramInputText::handle_event()
943 if(plugin->current_point >= 0 &&
944 plugin->current_point < plugin->config.points[plugin->mode].total())
946 HistogramPoint *point =
947 plugin->config.points[plugin->mode].get_item_number(
948 plugin->current_point);
953 point->x = atof(get_text());
955 point->y = atof(get_text());
957 plugin->config.boundaries();
958 gui->update_canvas();
960 HistogramWindow *window = (HistogramWindow *)plugin->thread->window;
961 window->output->update();
962 plugin->send_configure_change();
968 void HistogramInputText::update()
970 if(plugin->current_point >= 0 &&
971 plugin->current_point < plugin->config.points[plugin->mode].total())
973 HistogramPoint *point =
975 plugin->config.points[plugin->mode].get_item_number(
976 plugin->current_point);
981 BC_TumbleTextBox::update(point->x);
983 BC_TumbleTextBox::update(point->y);
987 BC_TumbleTextBox::update((float)0.0);
992 BC_TumbleTextBox::update((float)0.0);
998 HistogramSmoothMode::HistogramSmoothMode(HistogramMain*plugin,
999 HistogramWindow *gui,
1002 : BC_PopupMenu(x, y, 120, to_text(plugin->config.smoothMode), 1)
1004 this->plugin = plugin;
1007 void HistogramSmoothMode::create_objects()
1009 add_item(new BC_MenuItem(to_text(HISTOGRAM_LINEAR)));
1010 add_item(new BC_MenuItem(to_text(HISTOGRAM_POLYNOMINAL)));
1011 add_item(new BC_MenuItem(to_text(HISTOGRAM_BEZIER)));
1014 char* HistogramSmoothMode::to_text(int mode)
1018 case HISTOGRAM_LINEAR:
1020 case HISTOGRAM_POLYNOMINAL:
1021 return _("Polynominal");
1022 case HISTOGRAM_BEZIER:
1028 int HistogramSmoothMode::from_text(char *text)
1030 if(!strcmp(text, to_text(HISTOGRAM_LINEAR)))
1031 return HISTOGRAM_LINEAR;
1032 if(!strcmp(text, to_text(HISTOGRAM_POLYNOMINAL)))
1033 return HISTOGRAM_POLYNOMINAL;
1034 if(!strcmp(text, to_text(HISTOGRAM_BEZIER)))
1035 return HISTOGRAM_BEZIER;
1036 return HISTOGRAM_LINEAR;
1039 int HistogramSmoothMode::handle_event()
1041 plugin->config.smoothMode = from_text(get_text());
1042 gui->update_canvas();
1043 plugin->send_configure_change();