prevent popup deactivation while button_down
[goodguy/history.git] / cinelerra-5.0 / plugins / overlay / overlay.C
1
2 /*
3  * CINELERRA
4  * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #include "bcdisplayinfo.h"
23 #include "clip.h"
24 #include "bchash.h"
25 #include "filexml.h"
26 #include "guicast.h"
27 #include "keyframe.h"
28 #include "language.h"
29 #include "overlayframe.h"
30 #include "pluginvclient.h"
31 #include "vframe.h"
32
33 #include <string.h>
34 #include <stdint.h>
35
36
37 class Overlay;
38 class OverlayWindow;
39
40
41 class OverlayConfig
42 {
43 public:
44         OverlayConfig();
45
46         static const char* mode_to_text(int mode);
47         int mode;
48
49         static const char* direction_to_text(int direction);
50         int direction;
51         enum
52         {
53                 BOTTOM_FIRST,
54                 TOP_FIRST
55         };
56
57         static const char* output_to_text(int output_layer);
58         int output_layer;
59         enum
60         {
61                 TOP,
62                 BOTTOM
63         };
64 };
65
66 class OverlayMode : public BC_PopupMenu
67 {
68 public:
69         OverlayMode(Overlay *plugin,
70                 int x,
71                 int y);
72         void create_objects();
73         int handle_event();
74         Overlay *plugin;
75 };
76
77 class OverlayDirection : public BC_PopupMenu
78 {
79 public:
80         OverlayDirection(Overlay *plugin,
81                 int x,
82                 int y);
83         void create_objects();
84         int handle_event();
85         Overlay *plugin;
86 };
87
88 class OverlayOutput : public BC_PopupMenu
89 {
90 public:
91         OverlayOutput(Overlay *plugin,
92                 int x,
93                 int y);
94         void create_objects();
95         int handle_event();
96         Overlay *plugin;
97 };
98
99
100 class OverlayWindow : public PluginClientWindow
101 {
102 public:
103         OverlayWindow(Overlay *plugin);
104         ~OverlayWindow();
105
106         void create_objects();
107
108
109         Overlay *plugin;
110         OverlayMode *mode;
111         OverlayDirection *direction;
112         OverlayOutput *output;
113 };
114
115 class Overlay : public PluginVClient
116 {
117 public:
118         Overlay(PluginServer *server);
119         ~Overlay();
120
121
122         PLUGIN_CLASS_MEMBERS(OverlayConfig);
123
124         int process_buffer(VFrame **frame,
125                 int64_t start_position,
126                 double frame_rate);
127         int is_realtime();
128         int is_multichannel();
129         int is_synthesis();
130         void save_data(KeyFrame *keyframe);
131         void read_data(KeyFrame *keyframe);
132         void update_gui();
133         int handle_opengl();
134
135         OverlayFrame *overlayer;
136         VFrame *temp;
137         int current_layer;
138         int output_layer;
139 // Inclusive layer numbers
140         int input_layer1;
141         int input_layer2;
142 };
143
144 OverlayConfig::OverlayConfig()
145 {
146         mode = TRANSFER_NORMAL;
147         direction = OverlayConfig::BOTTOM_FIRST;
148         output_layer = OverlayConfig::TOP;
149 }
150
151 const char* OverlayConfig::mode_to_text(int mode)
152 {
153         switch(mode) {
154         case TRANSFER_NORMAL:           return _("Normal");
155         case TRANSFER_ADDITION:         return _("Addition");
156         case TRANSFER_SUBTRACT:         return _("Subtract");
157         case TRANSFER_MULTIPLY:         return _("Multiply");
158         case TRANSFER_DIVIDE:           return _("Divide");
159         case TRANSFER_REPLACE:          return _("Replace");
160         case TRANSFER_MAX:              return _("Max");
161         case TRANSFER_MIN:              return _("Min");
162         case TRANSFER_AVERAGE:          return _("Average");
163         case TRANSFER_DARKEN:           return _("Darken");
164         case TRANSFER_LIGHTEN:          return _("Lighten");
165         case TRANSFER_DST:              return _("Dst");
166         case TRANSFER_DST_ATOP:         return _("DstAtop");
167         case TRANSFER_DST_IN:           return _("DstIn");
168         case TRANSFER_DST_OUT:          return _("DstOut");
169         case TRANSFER_DST_OVER:         return _("DstOver");
170         case TRANSFER_SRC:              return _("Src");
171         case TRANSFER_SRC_ATOP:         return _("SrcAtop");
172         case TRANSFER_SRC_IN:           return _("SrcIn");
173         case TRANSFER_SRC_OUT:          return _("SrcOut");
174         case TRANSFER_SRC_OVER:         return _("SrcOver");
175         case TRANSFER_OR:               return _("Or");
176         case TRANSFER_XOR:              return _("Xor");
177         default:                        break;
178         }
179         return _("Normal");
180 }
181
182 const char* OverlayConfig::direction_to_text(int direction)
183 {
184         switch(direction)
185         {
186                 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
187                 case OverlayConfig::TOP_FIRST:    return _("Top first");
188         }
189         return "";
190 }
191
192 const char* OverlayConfig::output_to_text(int output_layer)
193 {
194         switch(output_layer)
195         {
196                 case OverlayConfig::TOP:    return _("Top");
197                 case OverlayConfig::BOTTOM: return _("Bottom");
198         }
199         return "";
200 }
201
202
203
204
205
206
207
208
209
210 OverlayWindow::OverlayWindow(Overlay *plugin)
211  : PluginClientWindow(plugin,
212         300,
213         160,
214         300,
215         160,
216         0)
217 {
218         this->plugin = plugin;
219 }
220
221 OverlayWindow::~OverlayWindow()
222 {
223 }
224
225 void OverlayWindow::create_objects()
226 {
227         int x = 10, y = 10;
228
229         BC_Title *title;
230         add_subwindow(title = new BC_Title(x, y, _("Mode:")));
231         add_subwindow(mode = new OverlayMode(plugin,
232                 x + title->get_w() + 5,
233                 y));
234         mode->create_objects();
235
236         y += 30;
237         add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
238         add_subwindow(direction = new OverlayDirection(plugin,
239                 x + title->get_w() + 5,
240                 y));
241         direction->create_objects();
242
243         y += 30;
244         add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
245         add_subwindow(output = new OverlayOutput(plugin,
246                 x + title->get_w() + 5,
247                 y));
248         output->create_objects();
249
250         show_window();
251         flush();
252 }
253
254
255
256
257
258
259
260 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
261  : BC_PopupMenu(x, y, 150,
262         OverlayConfig::mode_to_text(plugin->config.mode), 1)
263 {
264         this->plugin = plugin;
265 }
266
267 void OverlayMode::create_objects()
268 {
269         for(int i = 0; i < TRANSFER_TYPES; i++)
270                 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
271 }
272
273 int OverlayMode::handle_event()
274 {
275         char *text = get_text();
276
277         for(int i = 0; i < TRANSFER_TYPES; i++)
278         {
279                 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
280                 {
281                         plugin->config.mode = i;
282                         break;
283                 }
284         }
285
286         plugin->send_configure_change();
287         return 1;
288 }
289
290
291 OverlayDirection::OverlayDirection(Overlay *plugin,
292         int x,
293         int y)
294  : BC_PopupMenu(x,
295         y,
296         150,
297         OverlayConfig::direction_to_text(plugin->config.direction),
298         1)
299 {
300         this->plugin = plugin;
301 }
302
303 void OverlayDirection::create_objects()
304 {
305         add_item(new BC_MenuItem(
306                 OverlayConfig::direction_to_text(
307                         OverlayConfig::TOP_FIRST)));
308         add_item(new BC_MenuItem(
309                 OverlayConfig::direction_to_text(
310                         OverlayConfig::BOTTOM_FIRST)));
311 }
312
313 int OverlayDirection::handle_event()
314 {
315         char *text = get_text();
316
317         if(!strcmp(text,
318                 OverlayConfig::direction_to_text(
319                         OverlayConfig::TOP_FIRST)))
320                 plugin->config.direction = OverlayConfig::TOP_FIRST;
321         else
322         if(!strcmp(text,
323                 OverlayConfig::direction_to_text(
324                         OverlayConfig::BOTTOM_FIRST)))
325                 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
326
327         plugin->send_configure_change();
328         return 1;
329 }
330
331
332 OverlayOutput::OverlayOutput(Overlay *plugin,
333         int x,
334         int y)
335  : BC_PopupMenu(x,
336         y,
337         100,
338         OverlayConfig::output_to_text(plugin->config.output_layer),
339         1)
340 {
341         this->plugin = plugin;
342 }
343
344 void OverlayOutput::create_objects()
345 {
346         add_item(new BC_MenuItem(
347                 OverlayConfig::output_to_text(
348                         OverlayConfig::TOP)));
349         add_item(new BC_MenuItem(
350                 OverlayConfig::output_to_text(
351                         OverlayConfig::BOTTOM)));
352 }
353
354 int OverlayOutput::handle_event()
355 {
356         char *text = get_text();
357
358         if(!strcmp(text,
359                 OverlayConfig::output_to_text(
360                         OverlayConfig::TOP)))
361                 plugin->config.output_layer = OverlayConfig::TOP;
362         else
363         if(!strcmp(text,
364                 OverlayConfig::output_to_text(
365                         OverlayConfig::BOTTOM)))
366                 plugin->config.output_layer = OverlayConfig::BOTTOM;
367
368         plugin->send_configure_change();
369         return 1;
370 }
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391 REGISTER_PLUGIN(Overlay)
392
393
394
395
396
397
398 Overlay::Overlay(PluginServer *server)
399  : PluginVClient(server)
400 {
401
402         overlayer = 0;
403         temp = 0;
404 }
405
406
407 Overlay::~Overlay()
408 {
409
410         if(overlayer) delete overlayer;
411         if(temp) delete temp;
412 }
413
414
415
416 int Overlay::process_buffer(VFrame **frame,
417         int64_t start_position,
418         double frame_rate)
419 {
420         load_configuration();
421
422
423         if(!temp) temp = new VFrame(0,
424                 -1,
425                 frame[0]->get_w(),
426                 frame[0]->get_h(),
427                 frame[0]->get_color_model(),
428                 -1);
429
430         if(!overlayer)
431                 overlayer = new OverlayFrame(get_project_smp() + 1);
432
433         int step;
434         VFrame *output;
435
436         if(config.direction == OverlayConfig::BOTTOM_FIRST)
437         {
438                 input_layer1 = get_total_buffers() - 1;
439                 input_layer2 = -1;
440                 step = -1;
441         }
442         else
443         {
444                 input_layer1 = 0;
445                 input_layer2 = get_total_buffers();
446                 step = 1;
447         }
448
449         if(config.output_layer == OverlayConfig::TOP)
450         {
451                 output_layer = 0;
452         }
453         else
454         {
455                 output_layer = get_total_buffers() - 1;
456         }
457
458
459
460 // Direct copy the first layer
461         output = frame[output_layer];
462         read_frame(output,
463                 input_layer1,
464                 start_position,
465                 frame_rate,
466                 get_use_opengl());
467
468         if(get_total_buffers() == 1) return 0;
469
470
471
472         current_layer = input_layer1;
473         if(get_use_opengl())
474                 run_opengl();
475
476         for(int i = input_layer1 + step; i != input_layer2; i += step)
477         {
478                 read_frame(temp,
479                         i,
480                         start_position,
481                         frame_rate,
482                         get_use_opengl());
483
484 // Call the opengl handler once for each layer
485                 if(get_use_opengl())
486                 {
487                         current_layer = i;
488                         run_opengl();
489                 }
490                 else
491                 {
492                         overlayer->overlay(output,
493                                 temp,
494                                 0,
495                                 0,
496                                 output->get_w(),
497                                 output->get_h(),
498                                 0,
499                                 0,
500                                 output->get_w(),
501                                 output->get_h(),
502                                 1,
503                                 config.mode,
504                                 NEAREST_NEIGHBOR);
505                 }
506         }
507
508
509         return 0;
510 }
511
512 int Overlay::handle_opengl()
513 {
514 #ifdef HAVE_GL
515         static const char *get_pixels_frag =
516                 "uniform sampler2D src_tex;\n"
517                 "uniform sampler2D dst_tex;\n"
518                 "uniform vec2 dst_tex_dimensions;\n"
519                 "uniform vec3 chroma_offset;\n"
520                 "void main()\n"
521                 "{\n"
522                 "       vec4 result_color;\n"
523                 "       vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
524                 "       vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
525                 "       src_color.rgb -= chroma_offset;\n"
526                 "       dst_color.rgb -= chroma_offset;\n";
527
528         static const char *put_pixels_frag =
529                 "       result_color.rgb += chroma_offset;\n"
530                 "       result_color.rgb = mix(dst_color.rgb, result_color.rgb, src_color.a);\n"
531                 "       result_color.a = max(src_color.a, dst_color.a);\n"
532                 "       gl_FragColor = result_color;\n"
533                 "}\n";
534
535         static const char *blend_add_frag =
536                 "       result_color.rgb = dst_color.rgb + src_color.rgb;\n";
537
538         static const char *blend_max_frag =
539                 "       result_color.r = max(abs(dst_color.r, src_color.r);\n"
540                 "       result_color.g = max(abs(dst_color.g, src_color.g);\n"
541                 "       result_color.b = max(abs(dst_color.b, src_color.b);\n";
542
543         static const char *blend_min_frag =
544                 "       result_color.r = min(abs(dst_color.r, src_color.r);\n"
545                 "       result_color.g = min(abs(dst_color.g, src_color.g);\n"
546                 "       result_color.b = min(abs(dst_color.b, src_color.b);\n";
547
548         static const char *blend_subtract_frag =
549                 "       result_color.rgb = dst_color.rgb - src_color.rgb;\n";
550
551
552         static const char *blend_multiply_frag =
553                 "       result_color.rgb = dst_color.rgb * src_color.rgb;\n";
554
555         static const char *blend_divide_frag =
556                 "       result_color.rgb = dst_color.rgb / src_color.rgb;\n"
557                 "       if(src_color.r == 0.0) result_color.r = 1.0;\n"
558                 "       if(src_color.g == 0.0) result_color.g = 1.0;\n"
559                 "       if(src_color.b == 0.0) result_color.b = 1.0;\n";
560
561
562         VFrame *src = temp;
563         VFrame *dst = get_output(output_layer);
564
565         dst->enable_opengl();
566         dst->init_screen();
567
568         const char *shader_stack[] = { 0, 0, 0 };
569         int current_shader = 0;
570
571
572
573
574 // Direct copy layer
575         if(config.mode == TRANSFER_REPLACE)
576         {
577                 src->to_texture();
578                 src->bind_texture(0);
579                 dst->enable_opengl();
580                 dst->init_screen();
581
582 // Multiply alpha
583                 glDisable(GL_BLEND);
584                 src->draw_texture();
585         }
586         else
587         if(config.mode == TRANSFER_NORMAL)
588         {
589                 dst->enable_opengl();
590                 dst->init_screen();
591
592 // Move destination to screen
593                 if(dst->get_opengl_state() != VFrame::SCREEN)
594                 {
595                         dst->to_texture();
596                         dst->bind_texture(0);
597                         dst->draw_texture();
598                 }
599
600                 src->to_texture();
601                 src->bind_texture(0);
602                 dst->enable_opengl();
603                 dst->init_screen();
604
605                 glEnable(GL_BLEND);
606                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
607                 src->draw_texture();
608         }
609         else
610         {
611 // Read destination back to texture
612                 dst->to_texture();
613
614                 src->enable_opengl();
615                 src->init_screen();
616                 src->to_texture();
617
618                 dst->enable_opengl();
619                 dst->init_screen();
620                 src->bind_texture(0);
621                 dst->bind_texture(1);
622
623
624                 shader_stack[current_shader++] = get_pixels_frag;
625
626                 switch(config.mode)
627                 {
628                         case TRANSFER_ADDITION:
629                                 shader_stack[current_shader++] = blend_add_frag;
630                                 break;
631                         case TRANSFER_SUBTRACT:
632                                 shader_stack[current_shader++] = blend_subtract_frag;
633                                 break;
634                         case TRANSFER_MULTIPLY:
635                                 shader_stack[current_shader++] = blend_multiply_frag;
636                                 break;
637                         case TRANSFER_DIVIDE:
638                                 shader_stack[current_shader++] = blend_divide_frag;
639                                 break;
640                         case TRANSFER_MAX:
641                                 shader_stack[current_shader++] = blend_max_frag;
642                                 break;
643                         case TRANSFER_MIN:
644                                 shader_stack[current_shader++] = blend_min_frag;
645                                 break;
646                 }
647
648                 shader_stack[current_shader++] = put_pixels_frag;
649
650                 unsigned int shader_id = 0;
651                 shader_id = VFrame::make_shader(0,
652                         shader_stack[0],
653                         shader_stack[1],
654                         shader_stack[2],
655                         0);
656
657                 glUseProgram(shader_id);
658                 glUniform1i(glGetUniformLocation(shader_id, "src_tex"), 0);
659                 glUniform1i(glGetUniformLocation(shader_id, "dst_tex"), 1);
660                 if(BC_CModels::is_yuv(dst->get_color_model()))
661                         glUniform3f(glGetUniformLocation(shader_id, "chroma_offset"), 0.0, 0.5, 0.5);
662                 else
663                         glUniform3f(glGetUniformLocation(shader_id, "chroma_offset"), 0.0, 0.0, 0.0);
664                 glUniform2f(glGetUniformLocation(shader_id, "dst_tex_dimensions"),
665                         (float)dst->get_texture_w(),
666                         (float)dst->get_texture_h());
667
668                 glDisable(GL_BLEND);
669                 src->draw_texture();
670                 glUseProgram(0);
671         }
672
673         glDisable(GL_BLEND);
674         glActiveTexture(GL_TEXTURE1);
675         glDisable(GL_TEXTURE_2D);
676         glActiveTexture(GL_TEXTURE0);
677         glDisable(GL_TEXTURE_2D);
678
679         dst->set_opengl_state(VFrame::SCREEN);
680 #endif
681         return 0;
682 }
683
684
685 const char* Overlay::plugin_title() { return _("Overlay"); }
686 int Overlay::is_realtime() { return 1; }
687 int Overlay::is_multichannel() { return 1; }
688 int Overlay::is_synthesis() { return 1; }
689
690
691
692 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
693
694
695
696 int Overlay::load_configuration()
697 {
698         KeyFrame *prev_keyframe;
699         prev_keyframe = get_prev_keyframe(get_source_position());
700         read_data(prev_keyframe);
701         return 0;
702 }
703
704
705 void Overlay::save_data(KeyFrame *keyframe)
706 {
707         FileXML output;
708
709 // cause data to be stored directly in text
710         output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
711         output.tag.set_title("OVERLAY");
712         output.tag.set_property("MODE", config.mode);
713         output.tag.set_property("DIRECTION", config.direction);
714         output.tag.set_property("OUTPUT_LAYER", config.output_layer);
715         output.append_tag();
716         output.tag.set_title("/OVERLAY");
717         output.append_tag();
718         output.terminate_string();
719 }
720
721 void Overlay::read_data(KeyFrame *keyframe)
722 {
723         FileXML input;
724
725         input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
726
727         while(!input.read_tag())
728         {
729                 if(input.tag.title_is("OVERLAY"))
730                 {
731                         config.mode = input.tag.get_property("MODE", config.mode);
732                         config.direction = input.tag.get_property("DIRECTION", config.direction);
733                         config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
734                 }
735         }
736 }
737
738 void Overlay::update_gui()
739 {
740         if(thread)
741         {
742                 thread->window->lock_window("Overlay::update_gui");
743                 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
744                 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
745                 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
746                 thread->window->unlock_window();
747         }
748 }
749
750
751
752
753