r658: Initial revision
[cinelerra_cv.git] / plugins / histogram / histogramwindow.C
blob472b5fcf4d29246968dfe0a0ba9011198a2fa190
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "histogram.h"
4 #include "histogramconfig.h"
5 #include "histogramwindow.h"
6 #include "keys.h"
7 #include "language.h"
10 #include <unistd.h>
12 PLUGIN_THREAD_OBJECT(HistogramMain, HistogramThread, HistogramWindow)
16 HistogramWindow::HistogramWindow(HistogramMain *plugin, int x, int y)
17  : BC_Window(plugin->gui_string, 
18         x,
19         y,
20         440, 
21         480, 
22         440, 
23         480, 
24         0, 
25         1,
26         1)
28         this->plugin = plugin; 
31 HistogramWindow::~HistogramWindow()
35 #include "max_picon_png.h"
36 #include "mid_picon_png.h"
37 #include "min_picon_png.h"
38 static VFrame max_picon_image(max_picon_png);
39 static VFrame mid_picon_image(mid_picon_png);
40 static VFrame min_picon_image(min_picon_png);
42 int HistogramWindow::create_objects()
44         int x = 10, y = 10, x1 = 10;
45         BC_Title *title = 0;
47         max_picon = new BC_Pixmap(this, &max_picon_image);
48         mid_picon = new BC_Pixmap(this, &mid_picon_image);
49         min_picon = new BC_Pixmap(this, &min_picon_image);
50         add_subwindow(mode_v = new HistogramMode(plugin, 
51                 x, 
52                 y,
53                 HISTOGRAM_VALUE,
54                 _("Value")));
55         x += 70;
56         add_subwindow(mode_r = new HistogramMode(plugin, 
57                 x, 
58                 y,
59                 HISTOGRAM_RED,
60                 _("Red")));
61         x += 70;
62         add_subwindow(mode_g = new HistogramMode(plugin, 
63                 x, 
64                 y,
65                 HISTOGRAM_GREEN,
66                 _("Green")));
67         x += 70;
68         add_subwindow(mode_b = new HistogramMode(plugin, 
69                 x, 
70                 y,
71                 HISTOGRAM_BLUE,
72                 _("Blue")));
73 //      x += 70;
74 //      add_subwindow(mode_a = new HistogramMode(plugin, 
75 //              x, 
76 //              y,
77 //              HISTOGRAM_ALPHA,
78 //              _("Alpha")));
80         x = x1;
81         y += 30;
82         add_subwindow(title = new BC_Title(x, y, _("Input X:")));
83         x += title->get_w() + 10;
84         input_x = new HistogramInputText(plugin,
85                 this,
86                 x,
87                 y,
88                 1);
89         input_x->create_objects();
91         x += input_x->get_w() + 10;
92         add_subwindow(title = new BC_Title(x, y, _("Input Y:")));
93         x += title->get_w() + 10;
94         input_y = new HistogramInputText(plugin,
95                 this,
96                 x,
97                 y,
98                 0);
99         input_y->create_objects();
101         y += 30;
102         x = x1;
104         canvas_w = get_w() - x - x;
105         canvas_h = get_h() - y - 170;
106         title1_x = x;
107         title2_x = x + (int)(canvas_w * -MIN_INPUT / FLOAT_RANGE);
108         title3_x = x + (int)(canvas_w * (1.0 - MIN_INPUT) / FLOAT_RANGE);
109         title4_x = x + (int)(canvas_w);
110         add_subwindow(canvas = new HistogramCanvas(plugin,
111                 this,
112                 x, 
113                 y, 
114                 canvas_w, 
115                 canvas_h));
116         draw_canvas_overlay();
117         canvas->flash();
119         y += canvas->get_h() + 1;
120         add_subwindow(new BC_Title(title1_x, 
121                 y, 
122                 "-10%"));
123         add_subwindow(new BC_Title(title2_x,
124                 y,
125                 "0%"));
126         add_subwindow(new BC_Title(title3_x - get_text_width(MEDIUMFONT, "100"),
127                 y,
128                 "100%"));
129         add_subwindow(new BC_Title(title4_x - get_text_width(MEDIUMFONT, "110"),
130                 y,
131                 "110%"));
133         y += 20;
134         add_subwindow(title = new BC_Title(x, y, _("Output min:")));
135         x += title->get_w() + 10;
136         output_min = new HistogramOutputText(plugin,
137                 this,
138                 x,
139                 y,
140                 &plugin->config.output_min[plugin->mode]);
141         output_min->create_objects();
142         x += output_min->get_w() + 10;
143         add_subwindow(new BC_Title(x, y, _("Output Max:")));
144         x += title->get_w() + 10;
145         output_max = new HistogramOutputText(plugin,
146                 this,
147                 x,
148                 y,
149                 &plugin->config.output_max[plugin->mode]);
150         output_max->create_objects();
152         x = x1;
153         y += 30;
155         add_subwindow(output = new HistogramSlider(plugin, 
156                 this,
157                 x, 
158                 y, 
159                 get_w() - 20,
160                 30,
161                 0));
162         output->update();
163         y += 40;
166         add_subwindow(automatic = new HistogramAuto(plugin, 
167                 x, 
168                 y));
170         x += 120;
171         add_subwindow(new HistogramReset(plugin, 
172                 x, 
173                 y));
174         x += 100;
175         add_subwindow(new BC_Title(x, y, _("Threshold:")));
176         x += 100;
177         threshold = new HistogramOutputText(plugin,
178                 this,
179                 x,
180                 y,
181                 &plugin->config.threshold);
182         threshold->create_objects();
185         show_window();
187         return 0;
190 WINDOW_CLOSE_EVENT(HistogramWindow)
192 int HistogramWindow::keypress_event()
194         int result = 0;
195         if(get_keypress() == BACKSPACE ||
196                 get_keypress() == DELETE)
197         {
198                 if(plugin->current_point >= 0)
199                 {
200                         HistogramPoint *current = 
201                                 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
202                         delete current;
203                         plugin->current_point = -1;
204                         update_input();
205                         update_canvas();
206                         plugin->send_configure_change();
207                         result = 1;
208                 }
209         }
210         return result;
213 void HistogramWindow::update(int do_input)
215         automatic->update(plugin->config.automatic);
216         threshold->update(plugin->config.threshold);
217         update_mode();
219         if(do_input) update_input();
220         update_output();
223 void HistogramWindow::update_input()
225         input_x->update();
226         input_y->update();
229 void HistogramWindow::update_output()
231         output->update();
232         output_min->update(plugin->config.output_min[plugin->mode]);
233         output_max->update(plugin->config.output_max[plugin->mode]);
236 void HistogramWindow::update_mode()
238         mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
239         mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
240         mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
241         mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
242         output_min->output = &plugin->config.output_min[plugin->mode];
243         output_max->output = &plugin->config.output_max[plugin->mode];
246 void HistogramWindow::draw_canvas_overlay()
248         canvas->set_color(0x00ff00);
249         int y1;
251 // Calculate output curve
252         plugin->tabulate_curve(plugin->mode, 0);
254 // Draw output line
255         for(int i = 0; i < canvas_w; i++)
256         {
257                 float input = (float)i / 
258                                 canvas_w * 
259                                 FLOAT_RANGE + 
260                                 MIN_INPUT;
261                 float output = plugin->calculate_smooth(input, plugin->mode);
263                 int y2 = canvas_h - (int)(output * canvas_h);
264                 if(i > 0)
265                 {
266                         canvas->draw_line(i - 1, y1, i, y2);
267                 }
268                 y1 = y2;
269         }
271 // Draw output points
272         HistogramPoint *current = plugin->config.points[plugin->mode].first;
273         int number = 0;
274         while(current)
275         {
276                 int x = (int)((current->x - MIN_INPUT) * canvas_w / FLOAT_RANGE);
277                 int y = (int)(canvas_h - current->y * canvas_h);
278                 if(number == plugin->current_point)
279                         canvas->draw_box(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
280                 else
281                         canvas->draw_rectangle(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
282                 current = NEXT;
283                 number++;
284         }
287 // Draw 0 and 100% lines.
288         canvas->set_color(0xff0000);
289         canvas->draw_line(title2_x - canvas->get_x(), 
290                 0, 
291                 title2_x - canvas->get_x(), 
292                 canvas_h);
293         canvas->draw_line(title3_x - canvas->get_x(), 
294                 0, 
295                 title3_x - canvas->get_x(), 
296                 canvas_h);
299 void HistogramWindow::update_canvas()
301         int *accum = plugin->accum[plugin->mode];
302         int accum_per_canvas_i = HISTOGRAM_SLOTS / canvas_w + 1;
303         float accum_per_canvas_f = (float)HISTOGRAM_SLOTS / canvas_w;
304         int normalize = 0;
305         int max = 0;
307         for(int i = 0; i < HISTOGRAM_SLOTS; i++)
308         {
309                 if(accum && accum[i] > normalize) normalize = accum[i];
310         }
313         if(normalize)
314         {
315                 for(int i = 0; i < canvas_w; i++)
316                 {
317                         int accum_start = (int)(accum_per_canvas_f * i);
318                         int accum_end = accum_start + accum_per_canvas_i;
319                         max = 0;
320                         for(int j = accum_start; j < accum_end; j++)
321                         {
322                                 max = MAX(accum[j], max);
323                         }
325 //                      max = max * canvas_h / normalize;
326                         max = (int)(log(max) / log(normalize) * canvas_h);
328                         canvas->set_color(0xffffff);
329                         canvas->draw_line(i, 0, i, canvas_h - max);
330                         canvas->set_color(0x000000);
331                         canvas->draw_line(i, canvas_h - max, i, canvas_h);
332                 }
333         }
334         else
335         {
336                 canvas->set_color(0xffffff);
337                 canvas->draw_box(0, 0, canvas_w, canvas_h);
338         }
341         draw_canvas_overlay();
342         canvas->flash();
352 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
353         HistogramWindow *gui,
354         int x,
355         int y,
356         int w,
357         int h)
358  : BC_SubWindow(x,
359         y,
360         w,
361         h,
362         0xffffff)
364         this->plugin = plugin;
365         this->gui = gui;
368 int HistogramCanvas::button_press_event()
370         int result = 0;
371         if(is_event_win() && cursor_inside())
372         {
373                 if(!plugin->dragging_point)
374                 {
375                         HistogramPoint *new_point = 0;
376                         gui->deactivate();
377 // Search for existing point under cursor
378                         HistogramPoint *current = plugin->config.points[plugin->mode].first;
379                         plugin->current_point = -1;
380                         while(current)
381                         {
382                                 int x = (int)((current->x - MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
383                                 int y = (int)(gui->canvas_h - current->y * gui->canvas_h);
385                                 if(get_cursor_x() >= x - BOX_SIZE / 2 &&
386                                         get_cursor_y() >= y - BOX_SIZE / 2 &&
387                                         get_cursor_x() < x + BOX_SIZE / 2 &&
388                                         get_cursor_y() < y + BOX_SIZE / 2)
389                                 {
390                                         plugin->current_point = 
391                                                 plugin->config.points[plugin->mode].number_of(current);
392                                         plugin->point_x_offset = get_cursor_x() - x;
393                                         plugin->point_y_offset = get_cursor_y() - y;
394                                         break;
395                                 }
396                                 current = NEXT;
397                         }
399                         if(plugin->current_point < 0)
400                         {
401 // Create new point under cursor
402                                 float current_x = (float)get_cursor_x() * 
403                                         FLOAT_RANGE / 
404                                         get_w() + 
405                                         MIN_INPUT;
406                                 float current_y = 1.0 - 
407                                         (float)get_cursor_y() / 
408                                         get_h();
409                                 new_point = 
410                                         plugin->config.points[plugin->mode].insert(current_x, current_y);
411                                 plugin->current_point = 
412                                         plugin->config.points[plugin->mode].number_of(new_point);
413                                 plugin->point_x_offset = 0;
414                                 plugin->point_y_offset = 0;
415                         }
417                         plugin->dragging_point = 1;
418                         result = 1;
420                         plugin->config.boundaries();
421                         gui->update_input();
422                         gui->update_canvas();
423                         if(new_point)
424                         {
425                                 plugin->send_configure_change();
426                         }
427                 }
428         }
429         return result;
432 int HistogramCanvas::cursor_motion_event()
434         if(plugin->dragging_point)
435         {
436                 float current_x = 
437                         (float)(get_cursor_x() - plugin->point_x_offset) * 
438                         FLOAT_RANGE / 
439                         get_w() + 
440                         MIN_INPUT;
441                 float current_y = 1.0 - 
442                         (float)(get_cursor_y() - plugin->point_y_offset) / 
443                         get_h();
444                 HistogramPoint *current_point = 
445                         plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
446                 current_point->x = current_x;
447                 current_point->y = current_y;
448                 plugin->config.boundaries();
449                 gui->update_input();
450                 gui->update_canvas();
451                 plugin->send_configure_change();
452                 return 1;
453         }
454         return 0;
457 int HistogramCanvas::button_release_event()
459         if(plugin->dragging_point)
460         {
461 // Test for out of order points to delete.
462                 HistogramPoint *current = 
463                         plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
464                 HistogramPoint *prev = PREVIOUS;
465                 HistogramPoint *next = NEXT;
467                 if((prev && prev->x >= current->x) ||
468                         (next && next->x <= current->x))
469                 {
470                         delete current;
471                         plugin->current_point = -1;
472                         plugin->config.boundaries();
473                         gui->update_input();
474                         gui->update_canvas();
475                         plugin->send_configure_change();
476                 }
478                 plugin->dragging_point = 0;
479         }
480         return 0;
489 HistogramReset::HistogramReset(HistogramMain *plugin, 
490         int x,
491         int y)
492  : BC_GenericButton(x, y, _("Reset"))
494         this->plugin = plugin;
496 int HistogramReset::handle_event()
498         plugin->config.reset(0);
499         plugin->thread->window->update(1);
500         plugin->thread->window->update_canvas();
501         plugin->send_configure_change();
502         return 1;
513 HistogramSlider::HistogramSlider(HistogramMain *plugin, 
514         HistogramWindow *gui,
515         int x, 
516         int y, 
517         int w,
518         int h,
519         int is_input)
520  : BC_SubWindow(x, y, w, h)
522         this->plugin = plugin;
523         this->gui = gui;
524         this->is_input = is_input;
525         operation = NONE;
528 int HistogramSlider::input_to_pixel(float input)
530         return (int)((input - MIN_INPUT) / FLOAT_RANGE * get_w());
533 int HistogramSlider::button_press_event()
535         if(is_event_win() && cursor_inside())
536         {
537                 int min;
538                 int max;
539                 int w = get_w();
540                 int h = get_h();
541                 int half_h = get_h() / 2;
543                 gui->deactivate();
545                 if(operation == NONE)
546                 {
547                         int x1 = input_to_pixel(plugin->config.output_min[plugin->mode]) - 
548                                 gui->mid_picon->get_w() / 2;
549                         int x2 = x1 + gui->mid_picon->get_w();
550                         if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
551                                 get_cursor_y() >= half_h && get_cursor_y() < h)
552                         {
553                                 operation = DRAG_MIN_OUTPUT;
554                         }
555                 }
557                 if(operation == NONE)
558                 {
559                         int x1 = input_to_pixel(plugin->config.output_max[plugin->mode]) - 
560                                 gui->mid_picon->get_w() / 2;
561                         int x2 = x1 + gui->mid_picon->get_w();
562                         if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
563                                 get_cursor_y() >= half_h && get_cursor_y() < h)
564                         {
565                                 operation = DRAG_MAX_OUTPUT;
566                         }
567                 }
568                 return 1;
569         }
570         return 0;
573 int HistogramSlider::button_release_event()
575         if(operation != NONE)
576         {
577                 operation = NONE;
578                 return 1;
579         }
580         return 0;
583 int HistogramSlider::cursor_motion_event()
585         if(operation != NONE)
586         {
587                 float value = (float)get_cursor_x() / get_w() * FLOAT_RANGE + MIN_INPUT;
588                 CLAMP(value, MIN_INPUT, MAX_INPUT);
590                 switch(operation)
591                 {
592                         case DRAG_MIN_OUTPUT:
593                                 value = MIN(plugin->config.output_max[plugin->mode], value);
594                                 plugin->config.output_min[plugin->mode] = value;
595                                 break;
596                         case DRAG_MAX_OUTPUT:
597                                 value = MAX(plugin->config.output_min[plugin->mode], value);
598                                 plugin->config.output_max[plugin->mode] = value;
599                                 break;
600                 }
601         
602                 plugin->config.boundaries();
603                 gui->update_output();
605                 plugin->send_configure_change();
606                 return 1;
607         }
608         return 0;
611 void HistogramSlider::update()
613         int w = get_w();
614         int h = get_h();
615         int half_h = get_h() / 2;
616         int quarter_h = get_h() / 4;
617         int mode = plugin->mode;
618         int r = 0xff;
619         int g = 0xff;
620         int b = 0xff;
622         clear_box(0, 0, w, h);
624         switch(mode)
625         {
626                 case HISTOGRAM_RED:
627                         g = b = 0x00;
628                         break;
629                 case HISTOGRAM_GREEN:
630                         r = b = 0x00;
631                         break;
632                 case HISTOGRAM_BLUE:
633                         r = g = 0x00;
634                         break;
635         }
637         for(int i = 0; i < w; i++)
638         {
639                 int color = (int)(i * 0xff / w);
640                 set_color(((r * color / 0xff) << 16) | 
641                         ((g * color / 0xff) << 8) | 
642                         (b * color / 0xff));
644                 draw_line(i, 0, i, half_h);
645         }
647         float min;
648         float max;
649         min = plugin->config.output_min[plugin->mode];
650         max = plugin->config.output_max[plugin->mode];
652         draw_pixmap(gui->min_picon,
653                 input_to_pixel(min) - gui->min_picon->get_w() / 2,
654                 half_h + 1);
655         draw_pixmap(gui->max_picon,
656                 input_to_pixel(max) - gui->max_picon->get_w() / 2,
657                 half_h + 1);
659         flash();
660         flush();
671 HistogramAuto::HistogramAuto(HistogramMain *plugin, 
672         int x, 
673         int y)
674  : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
676         this->plugin = plugin;
679 int HistogramAuto::handle_event()
681         plugin->config.automatic = get_value();
682         plugin->send_configure_change();
683         return 1;
689 HistogramMode::HistogramMode(HistogramMain *plugin, 
690         int x, 
691         int y,
692         int value,
693         char *text)
694  : BC_Radial(x, y, plugin->mode == value, text)
696         this->plugin = plugin;
697         this->value = value;
699 int HistogramMode::handle_event()
701         plugin->mode = value;
702         plugin->current_point= -1;
703         plugin->thread->window->update_canvas();
704         plugin->thread->window->update_mode();
705         plugin->thread->window->update_input();
706         plugin->thread->window->update_canvas();
707         plugin->thread->window->update_output();
708         plugin->thread->window->output->update();
709 //      plugin->send_configure_change();
710         return 1;
721 HistogramOutputText::HistogramOutputText(HistogramMain *plugin,
722         HistogramWindow *gui,
723         int x,
724         int y,
725         float *output)
726  : BC_TumbleTextBox(gui, 
727                 output ? (float)*output : 0.0,
728                 (float)MIN_INPUT,
729                 (float)MAX_INPUT,
730                 x, 
731                 y, 
732                 60)
734         this->plugin = plugin;
735         this->output = output;
736         set_precision(DIGITS);
737         set_increment(PRECISION);
741 int HistogramOutputText::handle_event()
743         if(output)
744         {
745                 *output = atof(get_text());
746         }
748         plugin->thread->window->output->update();
749         plugin->send_configure_change();
750         return 1;
760 HistogramInputText::HistogramInputText(HistogramMain *plugin,
761         HistogramWindow *gui,
762         int x,
763         int y,
764         int do_x)
765  : BC_TumbleTextBox(gui, 
766                 0.0,
767                 (float)MIN_INPUT,
768                 (float)MAX_INPUT,
769                 x, 
770                 y, 
771                 60)
773         this->do_x = do_x;
774         this->plugin = plugin;
775         this->gui = gui;
776         set_precision(DIGITS);
777         set_increment(PRECISION);
781 int HistogramInputText::handle_event()
783         if(plugin->current_point >= 0 &&
784                 plugin->current_point < plugin->config.points[plugin->mode].total())
785         {
786                 HistogramPoint *point = 
787                         plugin->config.points[plugin->mode].get_item_number(
788                                 plugin->current_point);
790                 if(point)
791                 {
792                         if(do_x) 
793                                 point->x = atof(get_text());
794                         else
795                                 point->y = atof(get_text());
797                         plugin->config.boundaries();
798                         gui->update_canvas();
800                         plugin->thread->window->output->update();
801                         plugin->send_configure_change();
802                 }
803         }
804         return 1;
807 void HistogramInputText::update()
809         if(plugin->current_point >= 0 &&
810                 plugin->current_point < plugin->config.points[plugin->mode].total())
811         {
812                 HistogramPoint *point = 
814                         plugin->config.points[plugin->mode].get_item_number(
815                                 plugin->current_point);
817                 if(point)
818                 {
819                         if(do_x)
820                                 BC_TumbleTextBox::update(point->x);
821                         else
822                                 BC_TumbleTextBox::update(point->y);
823                 }
824                 else
825                 {
826                         BC_TumbleTextBox::update((float)0.0);
827                 }
828         }
829         else
830         {
831                 BC_TumbleTextBox::update((float)0.0);
832         }
833