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 "histogram.h"
25 #include "histogramconfig.h"
26 #include "histogramwindow.h"
34 PLUGIN_THREAD_OBJECT(HistogramMain, HistogramThread, HistogramWindow)
38 HistogramWindow::HistogramWindow(HistogramMain *plugin, int x, int y)
39 : BC_Window(plugin->gui_string,
50 this->plugin = plugin;
53 HistogramWindow::~HistogramWindow()
57 static VFrame max_picon_image(max_picon_png);
58 static VFrame mid_picon_image(mid_picon_png);
59 static VFrame min_picon_image(min_picon_png);
61 int HistogramWindow::create_objects()
63 int x = 10, y = 10, x1 = 10;
66 max_picon = new BC_Pixmap(this, &max_picon_image);
67 mid_picon = new BC_Pixmap(this, &mid_picon_image);
68 min_picon = new BC_Pixmap(this, &min_picon_image);
69 add_subwindow(mode_v = new HistogramMode(plugin,
75 add_subwindow(mode_r = new HistogramMode(plugin,
81 add_subwindow(mode_g = new HistogramMode(plugin,
87 add_subwindow(mode_b = new HistogramMode(plugin,
93 // add_subwindow(mode_a = new HistogramMode(plugin,
101 add_subwindow(title = new BC_Title(x, y, _("Input X:")));
102 x += title->get_w() + 10;
103 input_x = new HistogramInputText(plugin,
108 input_x->create_objects();
110 x += input_x->get_w() + 10;
111 add_subwindow(title = new BC_Title(x, y, _("Input Y:")));
112 x += title->get_w() + 10;
113 input_y = new HistogramInputText(plugin,
118 input_y->create_objects();
123 canvas_w = get_w() - x - x;
124 canvas_h = get_h() - y - 170;
126 title2_x = x + (int)(canvas_w * -HIST_MIN_INPUT / FLOAT_RANGE);
127 title3_x = x + (int)(canvas_w * (1.0 - HIST_MIN_INPUT) / FLOAT_RANGE);
128 title4_x = x + (int)(canvas_w);
129 add_subwindow(canvas = new HistogramCanvas(plugin,
135 draw_canvas_overlay();
138 y += canvas->get_h() + 1;
139 add_subwindow(new BC_Title(title1_x,
142 add_subwindow(new BC_Title(title2_x,
145 add_subwindow(new BC_Title(title3_x - get_text_width(MEDIUMFONT, "100"),
148 add_subwindow(new BC_Title(title4_x - get_text_width(MEDIUMFONT, "110"),
153 add_subwindow(title = new BC_Title(x, y, _("Output min:")));
154 x += title->get_w() + 10;
155 output_min = new HistogramOutputText(plugin,
159 &plugin->config.output_min[plugin->mode]);
160 output_min->create_objects();
161 x += output_min->get_w() + 10;
162 add_subwindow(new BC_Title(x, y, _("Output Max:")));
163 x += title->get_w() + 10;
164 output_max = new HistogramOutputText(plugin,
168 &plugin->config.output_max[plugin->mode]);
169 output_max->create_objects();
174 add_subwindow(output = new HistogramSlider(plugin,
185 add_subwindow(automatic = new HistogramAuto(plugin,
190 add_subwindow(new HistogramReset(plugin,
194 add_subwindow(new BC_Title(x, y, _("Threshold:")));
196 threshold = new HistogramOutputText(plugin,
200 &plugin->config.threshold);
201 threshold->create_objects();
204 add_subwindow(split = new HistogramSplit(plugin, x, y));
207 add_subwindow(new BC_Title(x,y, _("Interpolation:")));
209 add_subwindow(smoothModeChoser = new HistogramSmoothMode(plugin, this, x, y));
210 smoothModeChoser->create_objects();
217 WINDOW_CLOSE_EVENT(HistogramWindow)
219 int HistogramWindow::keypress_event()
222 if(get_keypress() == BACKSPACE ||
223 get_keypress() == DELETE)
225 if(plugin->current_point >= 0)
227 HistogramPoint *current =
228 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
230 plugin->current_point = -1;
233 plugin->send_configure_change();
240 void HistogramWindow::update(int do_input)
242 automatic->update(plugin->config.automatic);
243 threshold->update(plugin->config.threshold);
246 if(do_input) update_input();
250 void HistogramWindow::update_input()
256 void HistogramWindow::update_output()
259 output_min->update(plugin->config.output_min[plugin->mode]);
260 output_max->update(plugin->config.output_max[plugin->mode]);
263 void HistogramWindow::update_mode()
265 mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
266 mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
267 mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
268 mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
269 output_min->output = &plugin->config.output_min[plugin->mode];
270 output_max->output = &plugin->config.output_max[plugin->mode];
273 void HistogramWindow::draw_canvas_overlay()
275 canvas->set_color(0x00ff00);
278 // Calculate output curve
279 plugin->tabulate_curve(plugin->mode, 0);
282 for(int i = 0; i < canvas_w; i++)
284 float input = (float)i /
288 float output = plugin->calculate_smooth(input, plugin->mode);
290 int y2 = canvas_h - (int)(output * canvas_h);
293 canvas->draw_line(i - 1, y1, i, y2);
298 // Draw output points
299 HistogramPoint *current = plugin->config.points[plugin->mode].first;
303 int x = (int)((current->x - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
304 int y = (int)(canvas_h - current->y * canvas_h);
305 if(number == plugin->current_point)
306 canvas->draw_box(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
308 canvas->draw_rectangle(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
311 if (plugin->config.smoothMode > HISTOGRAM_LINEAR)
314 canvas->set_color(0x0000ff);
315 x2 = (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
316 x1 = (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * canvas_w / FLOAT_RANGE);
317 y2 = (int)(canvas_h - (current->y + current->xoffset_right * current->gradient) * canvas_h);
318 y1 = (int)(canvas_h - (current->y + current->xoffset_left * current->gradient) * canvas_h);
319 /* x2 = x + (title3_x - title2_x)/20;
320 x1 = x - (title3_x - title2_x)/20;
321 y1 = y + (int)(current->gradient * (float)(canvas_h)/20.0);
322 y2 = y - (int)(current->gradient * (float)(canvas_h)/20.0);
323 // int y2 = (int)(canvas_h - canvas_h * (current->y + current->gradient /10));*/
324 canvas->draw_line(x1,y1,x2,y2);
326 canvas->draw_circle(x1 - BOX_SIZE / 4, y1 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
327 canvas->draw_circle(x2 - BOX_SIZE / 4, y2 - BOX_SIZE / 4, BOX_SIZE/2, BOX_SIZE/2);
328 canvas->set_color(0x00ff00);
336 // Draw 0 and 100% lines.
337 canvas->set_color(0xff0000);
338 canvas->draw_line(title2_x - canvas->get_x(),
340 title2_x - canvas->get_x(),
342 canvas->draw_line(title3_x - canvas->get_x(),
344 title3_x - canvas->get_x(),
348 void HistogramWindow::update_canvas()
350 int *accum = plugin->accum[plugin->mode];
351 int accum_per_canvas_i = HISTOGRAM_SLOTS / canvas_w + 1;
352 float accum_per_canvas_f = (float)HISTOGRAM_SLOTS / canvas_w;
356 for(int i = 0; i < HISTOGRAM_SLOTS; i++)
358 if(accum && accum[i] > normalize) normalize = accum[i];
364 for(int i = 0; i < canvas_w; i++)
366 int accum_start = (int)(accum_per_canvas_f * i);
367 int accum_end = accum_start + accum_per_canvas_i;
369 for(int j = accum_start; j < accum_end; j++)
371 max = MAX(accum[j], max);
374 // max = max * canvas_h / normalize;
375 max = (int)(log(max) / log(normalize) * canvas_h);
377 canvas->set_color(0xffffff);
378 canvas->draw_line(i, 0, i, canvas_h - max);
379 canvas->set_color(0x000000);
380 canvas->draw_line(i, canvas_h - max, i, canvas_h);
385 canvas->set_color(0xffffff);
386 canvas->draw_box(0, 0, canvas_w, canvas_h);
390 draw_canvas_overlay();
401 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
402 HistogramWindow *gui,
413 this->plugin = plugin;
417 int HistogramCanvas::button_press_event()
420 if(is_event_win() && cursor_inside())
422 if(!plugin->dragging_point)
424 HistogramPoint *new_point = 0;
426 // Search for existing point under cursor
427 HistogramPoint *current = plugin->config.points[plugin->mode].first;
428 plugin->current_point = -1;
432 int x = (int)((current->x - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
433 int y = (int)(gui->canvas_h - current->y * gui->canvas_h);
435 /* Check for click on main point */
436 if(get_cursor_x() >= x - BOX_SIZE / 2 &&
437 get_cursor_y() >= y - BOX_SIZE / 2 &&
438 get_cursor_x() < x + BOX_SIZE / 2 &&
439 get_cursor_y() < y + BOX_SIZE / 2)
441 plugin->current_point =
442 plugin->config.points[plugin->mode].number_of(current);
443 plugin->point_x_offset = get_cursor_x() - x;
444 plugin->point_y_offset = get_cursor_y() - y;
448 if (plugin->config.smoothMode == HISTOGRAM_LINEAR)
452 (int)((current->x + current->xoffset_right - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
454 (int)(gui->canvas_h - (current->y + current->xoffset_right * current->gradient) *
457 /* Check for click on right handle */
458 if(get_cursor_x() >= xright - BOX_SIZE / 2 &&
459 get_cursor_y() >= yright - BOX_SIZE / 2 &&
460 get_cursor_x() < xright + BOX_SIZE / 2 &&
461 get_cursor_y() < yright + BOX_SIZE / 2)
463 plugin->current_point =
464 plugin->config.points[plugin->mode].number_of(current);
465 plugin->point_x_offset = get_cursor_x() - xright;
466 plugin->point_y_offset = get_cursor_y() - yright;
471 /* Check for click on left handle */
473 (int)((current->x + current->xoffset_left - HIST_MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
475 (int)(gui->canvas_h - (current->y + current->xoffset_left * current->gradient) *
477 if(get_cursor_x() >= xleft - BOX_SIZE / 2 &&
478 get_cursor_y() >= yleft - BOX_SIZE / 2 &&
479 get_cursor_x() < xleft + BOX_SIZE / 2 &&
480 get_cursor_y() < yleft + BOX_SIZE / 2)
482 plugin->current_point =
483 plugin->config.points[plugin->mode].number_of(current);
484 plugin->point_x_offset = get_cursor_x() - xleft;
485 plugin->point_y_offset = get_cursor_y() - yleft;
494 if(plugin->current_point < 0)
496 // Create new point under cursor
497 float current_x = (float)get_cursor_x() *
501 float current_y = 1.0 -
502 (float)get_cursor_y() /
505 plugin->config.points[plugin->mode].insert(current_x, current_y);
506 plugin->current_point =
507 plugin->config.points[plugin->mode].number_of(new_point);
508 plugin->point_x_offset = 0;
509 plugin->point_y_offset = 0;
512 // Get 2 points surrounding current position
515 HistogramPoint *current = plugin->config.points[plugin->mode].first;
517 while(current && !done)
519 if(current->x > current_x)
529 current = plugin->config.points[plugin->mode].last;
531 while(current && !done)
533 if(current->x <= current_x)
542 new_point->gradient = (y2 - y1) / (x2 - x1);
548 plugin->dragging_point = dragID;
551 plugin->config.boundaries();
553 gui->update_canvas();
556 plugin->send_configure_change();
563 int HistogramCanvas::cursor_motion_event()
565 if(plugin->dragging_point)
567 HistogramPoint * current_point = plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
570 (float)(get_cursor_x() - plugin->point_x_offset) *
574 float current_y = 1.0 -
575 (float)(get_cursor_y() - plugin->point_y_offset) /
578 switch(plugin->dragging_point)
580 case 1: /* Main point dragged */
581 current_point->x = current_x;
582 current_point->y = current_y;
584 case 2: /* Right control point dragged */
585 if (current_x - current_point->x > 0)
587 current_point->xoffset_right = current_x - current_point->x;
588 current_point->gradient = (current_y - current_point->y) / (current_x - current_point->x);
591 case 3: /* Left control point dragged */
592 if (current_x - current_point->x < 0)
594 current_point->xoffset_left = current_x - current_point->x;
595 current_point->gradient = (current_point->y - current_y) / (current_point->x - current_x);
600 plugin->config.boundaries();
602 gui->update_canvas();
603 plugin->send_configure_change();
609 int HistogramCanvas::button_release_event()
611 if(plugin->dragging_point)
613 // Test for out of order points to delete.
614 HistogramPoint *current =
615 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
616 HistogramPoint *prev = PREVIOUS;
617 HistogramPoint *next = NEXT;
619 if((prev && prev->x >= current->x) ||
620 (next && next->x <= current->x))
623 plugin->current_point = -1;
624 plugin->config.boundaries();
626 gui->update_canvas();
627 plugin->send_configure_change();
630 plugin->dragging_point = 0;
641 HistogramReset::HistogramReset(HistogramMain *plugin,
644 : BC_GenericButton(x, y, _("Reset"))
646 this->plugin = plugin;
648 int HistogramReset::handle_event()
650 plugin->config.reset(0);
651 plugin->thread->window->update(1);
652 plugin->thread->window->update_canvas();
653 plugin->send_configure_change();
665 HistogramSlider::HistogramSlider(HistogramMain *plugin,
666 HistogramWindow *gui,
672 : BC_SubWindow(x, y, w, h)
674 this->plugin = plugin;
676 this->is_input = is_input;
680 int HistogramSlider::input_to_pixel(float input)
682 return (int)((input - HIST_MIN_INPUT) / FLOAT_RANGE * get_w());
685 int HistogramSlider::button_press_event()
687 if(is_event_win() && cursor_inside())
693 int half_h = get_h() / 2;
697 if(operation == NONE)
699 int x1 = input_to_pixel(plugin->config.output_min[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_MIN_OUTPUT;
709 if(operation == NONE)
711 int x1 = input_to_pixel(plugin->config.output_max[plugin->mode]) -
712 gui->mid_picon->get_w() / 2;
713 int x2 = x1 + gui->mid_picon->get_w();
714 if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
715 get_cursor_y() >= half_h && get_cursor_y() < h)
717 operation = DRAG_MAX_OUTPUT;
725 int HistogramSlider::button_release_event()
727 if(operation != NONE)
735 int HistogramSlider::cursor_motion_event()
737 if(operation != NONE)
739 float value = (float)get_cursor_x() / get_w() * FLOAT_RANGE + HIST_MIN_INPUT;
740 CLAMP(value, HIST_MIN_INPUT, HIST_MAX_INPUT);
744 case DRAG_MIN_OUTPUT:
745 value = MIN(plugin->config.output_max[plugin->mode], value);
746 plugin->config.output_min[plugin->mode] = value;
748 case DRAG_MAX_OUTPUT:
749 value = MAX(plugin->config.output_min[plugin->mode], value);
750 plugin->config.output_max[plugin->mode] = value;
754 plugin->config.boundaries();
755 gui->update_output();
757 plugin->send_configure_change();
763 void HistogramSlider::update()
767 int half_h = get_h() / 2;
768 int quarter_h = get_h() / 4;
769 int mode = plugin->mode;
774 clear_box(0, 0, w, h);
781 case HISTOGRAM_GREEN:
789 for(int i = 0; i < w; i++)
791 int color = (int)(i * 0xff / w);
792 set_color(((r * color / 0xff) << 16) |
793 ((g * color / 0xff) << 8) |
796 draw_line(i, 0, i, half_h);
801 min = plugin->config.output_min[plugin->mode];
802 max = plugin->config.output_max[plugin->mode];
804 draw_pixmap(gui->min_picon,
805 input_to_pixel(min) - gui->min_picon->get_w() / 2,
807 draw_pixmap(gui->max_picon,
808 input_to_pixel(max) - gui->max_picon->get_w() / 2,
823 HistogramAuto::HistogramAuto(HistogramMain *plugin,
826 : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
828 this->plugin = plugin;
831 int HistogramAuto::handle_event()
833 plugin->config.automatic = get_value();
834 plugin->send_configure_change();
841 HistogramSplit::HistogramSplit(HistogramMain *plugin,
844 : BC_CheckBox(x, y, plugin->config.split, _("Split picture"))
846 this->plugin = plugin;
849 int HistogramSplit::handle_event()
851 plugin->config.split = get_value();
852 plugin->send_configure_change();
859 HistogramMode::HistogramMode(HistogramMain *plugin,
864 : BC_Radial(x, y, plugin->mode == value, text)
866 this->plugin = plugin;
869 int HistogramMode::handle_event()
871 plugin->mode = value;
872 plugin->current_point= -1;
873 plugin->thread->window->update_canvas();
874 plugin->thread->window->update_mode();
875 plugin->thread->window->update_input();
876 plugin->thread->window->update_canvas();
877 plugin->thread->window->update_output();
878 plugin->thread->window->output->update();
879 // plugin->send_configure_change();
891 HistogramOutputText::HistogramOutputText(HistogramMain *plugin,
892 HistogramWindow *gui,
896 : BC_TumbleTextBox(gui,
897 output ? (float)*output : 0.0,
898 (float)HIST_MIN_INPUT,
899 (float)HIST_MAX_INPUT,
904 this->plugin = plugin;
905 this->output = output;
906 set_precision(DIGITS);
907 set_increment(PRECISION);
911 int HistogramOutputText::handle_event()
915 *output = atof(get_text());
918 plugin->thread->window->output->update();
919 plugin->send_configure_change();
930 HistogramInputText::HistogramInputText(HistogramMain *plugin,
931 HistogramWindow *gui,
935 : BC_TumbleTextBox(gui,
937 (float)HIST_MIN_INPUT,
938 (float)HIST_MAX_INPUT,
944 this->plugin = plugin;
946 set_precision(DIGITS);
947 set_increment(PRECISION);
951 int HistogramInputText::handle_event()
953 if(plugin->current_point >= 0 &&
954 plugin->current_point < plugin->config.points[plugin->mode].total())
956 HistogramPoint *point =
957 plugin->config.points[plugin->mode].get_item_number(
958 plugin->current_point);
963 point->x = atof(get_text());
965 point->y = atof(get_text());
967 plugin->config.boundaries();
968 gui->update_canvas();
970 plugin->thread->window->output->update();
971 plugin->send_configure_change();
977 void HistogramInputText::update()
979 if(plugin->current_point >= 0 &&
980 plugin->current_point < plugin->config.points[plugin->mode].total())
982 HistogramPoint *point =
984 plugin->config.points[plugin->mode].get_item_number(
985 plugin->current_point);
990 BC_TumbleTextBox::update(point->x);
992 BC_TumbleTextBox::update(point->y);
996 BC_TumbleTextBox::update((float)0.0);
1001 BC_TumbleTextBox::update((float)0.0);
1007 HistogramSmoothMode::HistogramSmoothMode(HistogramMain*plugin,
1008 HistogramWindow *gui,
1011 : BC_PopupMenu(x, y, 120, to_text(plugin->config.smoothMode), 1)
1013 this->plugin = plugin;
1016 void HistogramSmoothMode::create_objects()
1018 add_item(new BC_MenuItem(to_text(HISTOGRAM_LINEAR)));
1019 add_item(new BC_MenuItem(to_text(HISTOGRAM_POLYNOMINAL)));
1020 add_item(new BC_MenuItem(to_text(HISTOGRAM_BEZIER)));
1023 char* HistogramSmoothMode::to_text(int mode)
1027 case HISTOGRAM_LINEAR:
1029 case HISTOGRAM_POLYNOMINAL:
1030 return _("Polynominal");
1031 case HISTOGRAM_BEZIER:
1035 int HistogramSmoothMode::from_text(char *text)
1037 if(!strcmp(text, to_text(HISTOGRAM_LINEAR)))
1038 return HISTOGRAM_LINEAR;
1039 if(!strcmp(text, to_text(HISTOGRAM_POLYNOMINAL)))
1040 return HISTOGRAM_POLYNOMINAL;
1041 if(!strcmp(text, to_text(HISTOGRAM_BEZIER)))
1042 return HISTOGRAM_BEZIER;
1045 int HistogramSmoothMode::handle_event()
1047 plugin->config.smoothMode = from_text(get_text());
1048 gui->update_canvas();
1049 plugin->send_configure_change();