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