r851: Merge 2.1:
[cinelerra_cv/ct.git] / plugins / histogram / histogramwindow.C
blobe179657ee83938a4c0e73f665e8afd8aeea5b638
1 #include "bcdisplayinfo.h"
2 #include "bcsignals.h"
3 #include "cursors.h"
4 #include "histogram.h"
5 #include "histogramconfig.h"
6 #include "histogramwindow.h"
7 #include "keys.h"
8 #include "language.h"
11 #include <unistd.h>
13 PLUGIN_THREAD_OBJECT(HistogramMain, HistogramThread, HistogramWindow)
17 HistogramWindow::HistogramWindow(HistogramMain *plugin, int x, int y)
18  : BC_Window(plugin->gui_string, 
19         x,
20         y,
21         440, 
22         480, 
23         440, 
24         480, 
25         0, 
26         1,
27         1)
29         this->plugin = plugin; 
32 HistogramWindow::~HistogramWindow()
36 #include "max_picon_png.h"
37 #include "mid_picon_png.h"
38 #include "min_picon_png.h"
39 static VFrame max_picon_image(max_picon_png);
40 static VFrame mid_picon_image(mid_picon_png);
41 static VFrame min_picon_image(min_picon_png);
43 int HistogramWindow::create_objects()
45         int x = 10, y = 10, x1 = 10;
46         BC_Title *title = 0;
48         max_picon = new BC_Pixmap(this, &max_picon_image);
49         mid_picon = new BC_Pixmap(this, &mid_picon_image);
50         min_picon = new BC_Pixmap(this, &min_picon_image);
51         add_subwindow(mode_v = new HistogramMode(plugin, 
52                 x, 
53                 y,
54                 HISTOGRAM_VALUE,
55                 _("Value")));
56         x += 70;
57         add_subwindow(mode_r = new HistogramMode(plugin, 
58                 x, 
59                 y,
60                 HISTOGRAM_RED,
61                 _("Red")));
62         x += 70;
63         add_subwindow(mode_g = new HistogramMode(plugin, 
64                 x, 
65                 y,
66                 HISTOGRAM_GREEN,
67                 _("Green")));
68         x += 70;
69         add_subwindow(mode_b = new HistogramMode(plugin, 
70                 x, 
71                 y,
72                 HISTOGRAM_BLUE,
73                 _("Blue")));
74 //      x += 70;
75 //      add_subwindow(mode_a = new HistogramMode(plugin, 
76 //              x, 
77 //              y,
78 //              HISTOGRAM_ALPHA,
79 //              _("Alpha")));
81         x = x1;
82         y += 30;
83         add_subwindow(title = new BC_Title(x, y, _("Input X:")));
84         x += title->get_w() + 10;
85         input_x = new HistogramInputText(plugin,
86                 this,
87                 x,
88                 y,
89                 1);
90         input_x->create_objects();
92         x += input_x->get_w() + 10;
93         add_subwindow(title = new BC_Title(x, y, _("Input Y:")));
94         x += title->get_w() + 10;
95         input_y = new HistogramInputText(plugin,
96                 this,
97                 x,
98                 y,
99                 0);
100         input_y->create_objects();
102         y += 30;
103         x = x1;
105         canvas_w = get_w() - x - x;
106         canvas_h = get_h() - y - 170;
107         title1_x = x;
108         title2_x = x + (int)(canvas_w * -MIN_INPUT / FLOAT_RANGE);
109         title3_x = x + (int)(canvas_w * (1.0 - MIN_INPUT) / FLOAT_RANGE);
110         title4_x = x + (int)(canvas_w);
111         add_subwindow(canvas = new HistogramCanvas(plugin,
112                 this,
113                 x, 
114                 y, 
115                 canvas_w, 
116                 canvas_h));
117         draw_canvas_overlay();
118         canvas->flash();
120         y += canvas->get_h() + 1;
121         add_subwindow(new BC_Title(title1_x, 
122                 y, 
123                 "-10%"));
124         add_subwindow(new BC_Title(title2_x,
125                 y,
126                 "0%"));
127         add_subwindow(new BC_Title(title3_x - get_text_width(MEDIUMFONT, "100"),
128                 y,
129                 "100%"));
130         add_subwindow(new BC_Title(title4_x - get_text_width(MEDIUMFONT, "110"),
131                 y,
132                 "110%"));
134         y += 20;
135         add_subwindow(title = new BC_Title(x, y, _("Output min:")));
136         x += title->get_w() + 10;
137         output_min = new HistogramOutputText(plugin,
138                 this,
139                 x,
140                 y,
141                 &plugin->config.output_min[plugin->mode]);
142         output_min->create_objects();
143         x += output_min->get_w() + 10;
144         add_subwindow(new BC_Title(x, y, _("Output Max:")));
145         x += title->get_w() + 10;
146         output_max = new HistogramOutputText(plugin,
147                 this,
148                 x,
149                 y,
150                 &plugin->config.output_max[plugin->mode]);
151         output_max->create_objects();
153         x = x1;
154         y += 30;
156         add_subwindow(output = new HistogramSlider(plugin, 
157                 this,
158                 x, 
159                 y, 
160                 get_w() - 20,
161                 30,
162                 0));
163         output->update();
164         y += 40;
167         add_subwindow(automatic = new HistogramAuto(plugin, 
168                 x, 
169                 y));
171         x += 120;
172         add_subwindow(new HistogramReset(plugin, 
173                 x, 
174                 y));
175         x += 100;
176         add_subwindow(new BC_Title(x, y, _("Threshold:")));
177         x += 100;
178         threshold = new HistogramOutputText(plugin,
179                 this,
180                 x,
181                 y,
182                 &plugin->config.threshold);
183         threshold->create_objects();
185         x = x1;
186         y += 40;        
187         add_subwindow(split = new HistogramSplit(plugin, x, y));
189         show_window();
191         return 0;
194 WINDOW_CLOSE_EVENT(HistogramWindow)
196 int HistogramWindow::keypress_event()
198         int result = 0;
199         if(get_keypress() == BACKSPACE ||
200                 get_keypress() == DELETE)
201         {
202                 if(plugin->current_point >= 0)
203                 {
204                         HistogramPoint *current = 
205                                 plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
206                         delete current;
207                         plugin->current_point = -1;
208                         update_input();
209                         update_canvas();
210                         plugin->send_configure_change();
211                         result = 1;
212                 }
213         }
214         return result;
217 void HistogramWindow::update(int do_input)
219         automatic->update(plugin->config.automatic);
220         threshold->update(plugin->config.threshold);
221         update_mode();
223         if(do_input) update_input();
224         update_output();
227 void HistogramWindow::update_input()
229         input_x->update();
230         input_y->update();
233 void HistogramWindow::update_output()
235         output->update();
236         output_min->update(plugin->config.output_min[plugin->mode]);
237         output_max->update(plugin->config.output_max[plugin->mode]);
240 void HistogramWindow::update_mode()
242         mode_v->update(plugin->mode == HISTOGRAM_VALUE ? 1 : 0);
243         mode_r->update(plugin->mode == HISTOGRAM_RED ? 1 : 0);
244         mode_g->update(plugin->mode == HISTOGRAM_GREEN ? 1 : 0);
245         mode_b->update(plugin->mode == HISTOGRAM_BLUE ? 1 : 0);
246         output_min->output = &plugin->config.output_min[plugin->mode];
247         output_max->output = &plugin->config.output_max[plugin->mode];
250 void HistogramWindow::draw_canvas_overlay()
252         canvas->set_color(0x00ff00);
253         int y1;
255 // Calculate output curve
256         plugin->tabulate_curve(plugin->mode, 0);
258 // Draw output line
259         for(int i = 0; i < canvas_w; i++)
260         {
261                 float input = (float)i / 
262                                 canvas_w * 
263                                 FLOAT_RANGE + 
264                                 MIN_INPUT;
265                 float output = plugin->calculate_smooth(input, plugin->mode);
267                 int y2 = canvas_h - (int)(output * canvas_h);
268                 if(i > 0)
269                 {
270                         canvas->draw_line(i - 1, y1, i, y2);
271                 }
272                 y1 = y2;
273         }
275 // Draw output points
276         HistogramPoint *current = plugin->config.points[plugin->mode].first;
277         int number = 0;
278         while(current)
279         {
280                 int x = (int)((current->x - MIN_INPUT) * canvas_w / FLOAT_RANGE);
281                 int y = (int)(canvas_h - current->y * canvas_h);
282                 if(number == plugin->current_point)
283                         canvas->draw_box(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
284                 else
285                         canvas->draw_rectangle(x - BOX_SIZE / 2, y - BOX_SIZE / 2, BOX_SIZE, BOX_SIZE);
286                 current = NEXT;
287                 number++;
288         }
291 // Draw 0 and 100% lines.
292         canvas->set_color(0xff0000);
293         canvas->draw_line(title2_x - canvas->get_x(), 
294                 0, 
295                 title2_x - canvas->get_x(), 
296                 canvas_h);
297         canvas->draw_line(title3_x - canvas->get_x(), 
298                 0, 
299                 title3_x - canvas->get_x(), 
300                 canvas_h);
303 void HistogramWindow::update_canvas()
305         int *accum = plugin->accum[plugin->mode];
306         int accum_per_canvas_i = HISTOGRAM_SLOTS / canvas_w + 1;
307         float accum_per_canvas_f = (float)HISTOGRAM_SLOTS / canvas_w;
308         int normalize = 0;
309         int max = 0;
311         for(int i = 0; i < HISTOGRAM_SLOTS; i++)
312         {
313                 if(accum && accum[i] > normalize) normalize = accum[i];
314         }
317         if(normalize)
318         {
319                 for(int i = 0; i < canvas_w; i++)
320                 {
321                         int accum_start = (int)(accum_per_canvas_f * i);
322                         int accum_end = accum_start + accum_per_canvas_i;
323                         max = 0;
324                         for(int j = accum_start; j < accum_end; j++)
325                         {
326                                 max = MAX(accum[j], max);
327                         }
329 //                      max = max * canvas_h / normalize;
330                         max = (int)(log(max) / log(normalize) * canvas_h);
332                         canvas->set_color(0xffffff);
333                         canvas->draw_line(i, 0, i, canvas_h - max);
334                         canvas->set_color(0x000000);
335                         canvas->draw_line(i, canvas_h - max, i, canvas_h);
336                 }
337         }
338         else
339         {
340                 canvas->set_color(0xffffff);
341                 canvas->draw_box(0, 0, canvas_w, canvas_h);
342         }
345         draw_canvas_overlay();
346         canvas->flash();
356 HistogramCanvas::HistogramCanvas(HistogramMain *plugin,
357         HistogramWindow *gui,
358         int x,
359         int y,
360         int w,
361         int h)
362  : BC_SubWindow(x,
363         y,
364         w,
365         h,
366         0xffffff)
368         this->plugin = plugin;
369         this->gui = gui;
372 int HistogramCanvas::button_press_event()
374         int result = 0;
375         if(is_event_win() && cursor_inside())
376         {
377                 if(!plugin->dragging_point)
378                 {
379                         HistogramPoint *new_point = 0;
380                         gui->deactivate();
381 // Search for existing point under cursor
382                         HistogramPoint *current = plugin->config.points[plugin->mode].first;
383                         plugin->current_point = -1;
384                         while(current)
385                         {
386                                 int x = (int)((current->x - MIN_INPUT) * gui->canvas_w / FLOAT_RANGE);
387                                 int y = (int)(gui->canvas_h - current->y * gui->canvas_h);
389                                 if(get_cursor_x() >= x - BOX_SIZE / 2 &&
390                                         get_cursor_y() >= y - BOX_SIZE / 2 &&
391                                         get_cursor_x() < x + BOX_SIZE / 2 &&
392                                         get_cursor_y() < y + BOX_SIZE / 2)
393                                 {
394                                         plugin->current_point = 
395                                                 plugin->config.points[plugin->mode].number_of(current);
396                                         plugin->point_x_offset = get_cursor_x() - x;
397                                         plugin->point_y_offset = get_cursor_y() - y;
398                                         break;
399                                 }
400                                 current = NEXT;
401                         }
403                         if(plugin->current_point < 0)
404                         {
405 // Create new point under cursor
406                                 float current_x = (float)get_cursor_x() * 
407                                         FLOAT_RANGE / 
408                                         get_w() + 
409                                         MIN_INPUT;
410                                 float current_y = 1.0 - 
411                                         (float)get_cursor_y() / 
412                                         get_h();
413                                 new_point = 
414                                         plugin->config.points[plugin->mode].insert(current_x, current_y);
415                                 plugin->current_point = 
416                                         plugin->config.points[plugin->mode].number_of(new_point);
417                                 plugin->point_x_offset = 0;
418                                 plugin->point_y_offset = 0;
419                         }
421                         plugin->dragging_point = 1;
422                         result = 1;
424                         plugin->config.boundaries();
425                         gui->update_input();
426                         gui->update_canvas();
427                         if(new_point)
428                         {
429                                 plugin->send_configure_change();
430                         }
431                 }
432         }
433         return result;
436 int HistogramCanvas::cursor_motion_event()
438         if(plugin->dragging_point)
439         {
440                 float current_x = 
441                         (float)(get_cursor_x() - plugin->point_x_offset) * 
442                         FLOAT_RANGE / 
443                         get_w() + 
444                         MIN_INPUT;
445                 float current_y = 1.0 - 
446                         (float)(get_cursor_y() - plugin->point_y_offset) / 
447                         get_h();
448                 HistogramPoint *current_point = 
449                         plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
450                 current_point->x = current_x;
451                 current_point->y = current_y;
452                 plugin->config.boundaries();
453                 gui->update_input();
454                 gui->update_canvas();
455                 plugin->send_configure_change();
456                 return 1;
457         }
458         return 0;
461 int HistogramCanvas::button_release_event()
463         if(plugin->dragging_point)
464         {
465 // Test for out of order points to delete.
466                 HistogramPoint *current = 
467                         plugin->config.points[plugin->mode].get_item_number(plugin->current_point);
468                 HistogramPoint *prev = PREVIOUS;
469                 HistogramPoint *next = NEXT;
471                 if((prev && prev->x >= current->x) ||
472                         (next && next->x <= current->x))
473                 {
474                         delete current;
475                         plugin->current_point = -1;
476                         plugin->config.boundaries();
477                         gui->update_input();
478                         gui->update_canvas();
479                         plugin->send_configure_change();
480                 }
482                 plugin->dragging_point = 0;
483         }
484         return 0;
493 HistogramReset::HistogramReset(HistogramMain *plugin, 
494         int x,
495         int y)
496  : BC_GenericButton(x, y, _("Reset"))
498         this->plugin = plugin;
500 int HistogramReset::handle_event()
502         plugin->config.reset(0);
503         plugin->thread->window->update(1);
504         plugin->thread->window->update_canvas();
505         plugin->send_configure_change();
506         return 1;
517 HistogramSlider::HistogramSlider(HistogramMain *plugin, 
518         HistogramWindow *gui,
519         int x, 
520         int y, 
521         int w,
522         int h,
523         int is_input)
524  : BC_SubWindow(x, y, w, h)
526         this->plugin = plugin;
527         this->gui = gui;
528         this->is_input = is_input;
529         operation = NONE;
532 int HistogramSlider::input_to_pixel(float input)
534         return (int)((input - MIN_INPUT) / FLOAT_RANGE * get_w());
537 int HistogramSlider::button_press_event()
539         if(is_event_win() && cursor_inside())
540         {
541                 int min;
542                 int max;
543                 int w = get_w();
544                 int h = get_h();
545                 int half_h = get_h() / 2;
547                 gui->deactivate();
549                 if(operation == NONE)
550                 {
551                         int x1 = input_to_pixel(plugin->config.output_min[plugin->mode]) - 
552                                 gui->mid_picon->get_w() / 2;
553                         int x2 = x1 + gui->mid_picon->get_w();
554                         if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
555                                 get_cursor_y() >= half_h && get_cursor_y() < h)
556                         {
557                                 operation = DRAG_MIN_OUTPUT;
558                         }
559                 }
561                 if(operation == NONE)
562                 {
563                         int x1 = input_to_pixel(plugin->config.output_max[plugin->mode]) - 
564                                 gui->mid_picon->get_w() / 2;
565                         int x2 = x1 + gui->mid_picon->get_w();
566                         if(get_cursor_x() >= x1 && get_cursor_x() < x2 &&
567                                 get_cursor_y() >= half_h && get_cursor_y() < h)
568                         {
569                                 operation = DRAG_MAX_OUTPUT;
570                         }
571                 }
572                 return 1;
573         }
574         return 0;
577 int HistogramSlider::button_release_event()
579         if(operation != NONE)
580         {
581                 operation = NONE;
582                 return 1;
583         }
584         return 0;
587 int HistogramSlider::cursor_motion_event()
589         if(operation != NONE)
590         {
591                 float value = (float)get_cursor_x() / get_w() * FLOAT_RANGE + MIN_INPUT;
592                 CLAMP(value, MIN_INPUT, MAX_INPUT);
594                 switch(operation)
595                 {
596                         case DRAG_MIN_OUTPUT:
597                                 value = MIN(plugin->config.output_max[plugin->mode], value);
598                                 plugin->config.output_min[plugin->mode] = value;
599                                 break;
600                         case DRAG_MAX_OUTPUT:
601                                 value = MAX(plugin->config.output_min[plugin->mode], value);
602                                 plugin->config.output_max[plugin->mode] = value;
603                                 break;
604                 }
605         
606                 plugin->config.boundaries();
607                 gui->update_output();
609                 plugin->send_configure_change();
610                 return 1;
611         }
612         return 0;
615 void HistogramSlider::update()
617         int w = get_w();
618         int h = get_h();
619         int half_h = get_h() / 2;
620         int quarter_h = get_h() / 4;
621         int mode = plugin->mode;
622         int r = 0xff;
623         int g = 0xff;
624         int b = 0xff;
626         clear_box(0, 0, w, h);
628         switch(mode)
629         {
630                 case HISTOGRAM_RED:
631                         g = b = 0x00;
632                         break;
633                 case HISTOGRAM_GREEN:
634                         r = b = 0x00;
635                         break;
636                 case HISTOGRAM_BLUE:
637                         r = g = 0x00;
638                         break;
639         }
641         for(int i = 0; i < w; i++)
642         {
643                 int color = (int)(i * 0xff / w);
644                 set_color(((r * color / 0xff) << 16) | 
645                         ((g * color / 0xff) << 8) | 
646                         (b * color / 0xff));
648                 draw_line(i, 0, i, half_h);
649         }
651         float min;
652         float max;
653         min = plugin->config.output_min[plugin->mode];
654         max = plugin->config.output_max[plugin->mode];
656         draw_pixmap(gui->min_picon,
657                 input_to_pixel(min) - gui->min_picon->get_w() / 2,
658                 half_h + 1);
659         draw_pixmap(gui->max_picon,
660                 input_to_pixel(max) - gui->max_picon->get_w() / 2,
661                 half_h + 1);
663         flash();
664         flush();
675 HistogramAuto::HistogramAuto(HistogramMain *plugin, 
676         int x, 
677         int y)
678  : BC_CheckBox(x, y, plugin->config.automatic, _("Automatic"))
680         this->plugin = plugin;
683 int HistogramAuto::handle_event()
685         plugin->config.automatic = get_value();
686         plugin->send_configure_change();
687         return 1;
693 HistogramSplit::HistogramSplit(HistogramMain *plugin, 
694         int x, 
695         int y)
696  : BC_CheckBox(x, y, plugin->config.split, _("Split picture"))
698         this->plugin = plugin;
701 int HistogramSplit::handle_event()
703         plugin->config.split = get_value();
704         plugin->send_configure_change();
705         return 1;
710 HistogramMode::HistogramMode(HistogramMain *plugin, 
711         int x, 
712         int y,
713         int value,
714         char *text)
715  : BC_Radial(x, y, plugin->mode == value, text)
717         this->plugin = plugin;
718         this->value = value;
720 int HistogramMode::handle_event()
722         plugin->mode = value;
723         plugin->current_point= -1;
724         plugin->thread->window->update_canvas();
725         plugin->thread->window->update_mode();
726         plugin->thread->window->update_input();
727         plugin->thread->window->update_canvas();
728         plugin->thread->window->update_output();
729         plugin->thread->window->output->update();
730 //      plugin->send_configure_change();
731         return 1;
742 HistogramOutputText::HistogramOutputText(HistogramMain *plugin,
743         HistogramWindow *gui,
744         int x,
745         int y,
746         float *output)
747  : BC_TumbleTextBox(gui, 
748                 output ? (float)*output : 0.0,
749                 (float)MIN_INPUT,
750                 (float)MAX_INPUT,
751                 x, 
752                 y, 
753                 60)
755         this->plugin = plugin;
756         this->output = output;
757         set_precision(DIGITS);
758         set_increment(PRECISION);
762 int HistogramOutputText::handle_event()
764         if(output)
765         {
766                 *output = atof(get_text());
767         }
769         plugin->thread->window->output->update();
770         plugin->send_configure_change();
771         return 1;
781 HistogramInputText::HistogramInputText(HistogramMain *plugin,
782         HistogramWindow *gui,
783         int x,
784         int y,
785         int do_x)
786  : BC_TumbleTextBox(gui, 
787                 0.0,
788                 (float)MIN_INPUT,
789                 (float)MAX_INPUT,
790                 x, 
791                 y, 
792                 60)
794         this->do_x = do_x;
795         this->plugin = plugin;
796         this->gui = gui;
797         set_precision(DIGITS);
798         set_increment(PRECISION);
802 int HistogramInputText::handle_event()
804         if(plugin->current_point >= 0 &&
805                 plugin->current_point < plugin->config.points[plugin->mode].total())
806         {
807                 HistogramPoint *point = 
808                         plugin->config.points[plugin->mode].get_item_number(
809                                 plugin->current_point);
811                 if(point)
812                 {
813                         if(do_x) 
814                                 point->x = atof(get_text());
815                         else
816                                 point->y = atof(get_text());
818                         plugin->config.boundaries();
819                         gui->update_canvas();
821                         plugin->thread->window->output->update();
822                         plugin->send_configure_change();
823                 }
824         }
825         return 1;
828 void HistogramInputText::update()
830         if(plugin->current_point >= 0 &&
831                 plugin->current_point < plugin->config.points[plugin->mode].total())
832         {
833                 HistogramPoint *point = 
835                         plugin->config.points[plugin->mode].get_item_number(
836                                 plugin->current_point);
838                 if(point)
839                 {
840                         if(do_x)
841                                 BC_TumbleTextBox::update(point->x);
842                         else
843                                 BC_TumbleTextBox::update(point->y);
844                 }
845                 else
846                 {
847                         BC_TumbleTextBox::update((float)0.0);
848                 }
849         }
850         else
851         {
852                 BC_TumbleTextBox::update((float)0.0);
853         }
854