Condense the xml-cleanup into a short feature-branch
[cinelerra_cv/mob.git] / plugins / overlay / overlay.C
blob5a6ff63b65bd84390fcab3ff2d24087514b08c85
1 #include "bcdisplayinfo.h"
2 #include "clip.h"
3 #include "bchash.h"
4 #include "filexml.h"
5 #include "guicast.h"
6 #include "keyframe.h"
7 #include "language.h"
8 #include "overlayframe.h"
9 #include "picon_png.h"
10 #include "pluginvclient.h"
11 #include "vframe.h"
13 #include <string.h>
14 #include <stdint.h>
17 class Overlay;
18 class OverlayWindow;
21 class OverlayConfig
23 public:
24         OverlayConfig();
28         static char* mode_to_text(int mode);
29         int mode;
31         static char* direction_to_text(int direction);
32         int direction;
33         enum
34         {
35                 BOTTOM_FIRST,
36                 TOP_FIRST
37         };
39         static char* output_to_text(int output_layer);
40         int output_layer;
41         enum
42         {
43                 TOP,
44                 BOTTOM
45         };
52 class OverlayMode : public BC_PopupMenu
54 public:
55         OverlayMode(Overlay *plugin,
56                 int x, 
57                 int y);
58         void create_objects();
59         int handle_event();
60         Overlay *plugin;
63 class OverlayDirection : public BC_PopupMenu
65 public:
66         OverlayDirection(Overlay *plugin,
67                 int x, 
68                 int y);
69         void create_objects();
70         int handle_event();
71         Overlay *plugin;
74 class OverlayOutput : public BC_PopupMenu
76 public:
77         OverlayOutput(Overlay *plugin,
78                 int x, 
79                 int y);
80         void create_objects();
81         int handle_event();
82         Overlay *plugin;
86 class OverlayWindow : public BC_Window
88 public:
89         OverlayWindow(Overlay *plugin, int x, int y);
90         ~OverlayWindow();
92         void create_objects();
93         int close_event();
95         Overlay *plugin;
96         OverlayMode *mode;
97         OverlayDirection *direction;
98         OverlayOutput *output;
102 PLUGIN_THREAD_HEADER(Overlay, OverlayThread, OverlayWindow)
106 class Overlay : public PluginVClient
108 public:
109         Overlay(PluginServer *server);
110         ~Overlay();
113         PLUGIN_CLASS_MEMBERS(OverlayConfig, OverlayThread);
115         int process_buffer(VFrame **frame,
116                 int64_t start_position,
117                 double frame_rate);
118         int is_realtime();
119         int is_multichannel();
120         int is_synthesis();
121         int load_defaults();
122         int save_defaults();
123         void save_data(KeyFrame *keyframe);
124         void read_data(KeyFrame *keyframe);
125         void update_gui();
126         int handle_opengl();
128         OverlayFrame *overlayer;
129         VFrame *temp;
130         int current_layer;
131         int output_layer;
132 // Inclusive layer numbers
133         int input_layer1;
134         int input_layer2;
148 OverlayConfig::OverlayConfig()
150         mode = TRANSFER_NORMAL;
151         direction = OverlayConfig::BOTTOM_FIRST;
152         output_layer = OverlayConfig::TOP;
155 char* OverlayConfig::mode_to_text(int mode)
157         switch(mode)
158         {
159                 case TRANSFER_NORMAL:
160                         return "Normal";
161                         break;
163                 case TRANSFER_REPLACE:
164                         return "Replace";
165                         break;
167                 case TRANSFER_ADDITION:
168                         return "Addition";
169                         break;
171                 case TRANSFER_SUBTRACT:
172                         return "Subtract";
173                         break;
175                 case TRANSFER_MULTIPLY:
176                         return "Multiply";
177                         break;
179                 case TRANSFER_DIVIDE:
180                         return "Divide";
181                         break;
183                 case TRANSFER_MAX:
184                         return "Max";
185                         break;
187                 default:
188                         return "Normal";
189                         break;
190         }
191         return "";
194 char* OverlayConfig::direction_to_text(int direction)
196         switch(direction)
197         {
198                 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
199                 case OverlayConfig::TOP_FIRST:    return _("Top first");
200         }
201         return "";
204 char* OverlayConfig::output_to_text(int output_layer)
206         switch(output_layer)
207         {
208                 case OverlayConfig::TOP:    return _("Top");
209                 case OverlayConfig::BOTTOM: return _("Bottom");
210         }
211         return "";
222 OverlayWindow::OverlayWindow(Overlay *plugin, int x, int y)
223  : BC_Window(plugin->gui_string, 
224         x, 
225         y, 
226         300, 
227         160, 
228         300, 
229         160, 
230         0, 
231         0,
232         1)
234         this->plugin = plugin;
237 OverlayWindow::~OverlayWindow()
241 void OverlayWindow::create_objects()
243         int x = 10, y = 10;
245         BC_Title *title;
246         add_subwindow(title = new BC_Title(x, y, _("Mode:")));
247         add_subwindow(mode = new OverlayMode(plugin, 
248                 x + title->get_w() + 5, 
249                 y));
250         mode->create_objects();
252         y += 30;
253         add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
254         add_subwindow(direction = new OverlayDirection(plugin, 
255                 x + title->get_w() + 5, 
256                 y));
257         direction->create_objects();
259         y += 30;
260         add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
261         add_subwindow(output = new OverlayOutput(plugin, 
262                 x + title->get_w() + 5, 
263                 y));
264         output->create_objects();
266         show_window();
267         flush();
270 WINDOW_CLOSE_EVENT(OverlayWindow)
276 OverlayMode::OverlayMode(Overlay *plugin,
277         int x, 
278         int y)
279  : BC_PopupMenu(x,
280         y,
281         150,
282         OverlayConfig::mode_to_text(plugin->config.mode),
283         1)
285         this->plugin = plugin;
288 void OverlayMode::create_objects()
290         for(int i = 0; i < TRANSFER_TYPES; i++)
291                 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
294 int OverlayMode::handle_event()
296         char *text = get_text();
298         for(int i = 0; i < TRANSFER_TYPES; i++)
299         {
300                 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
301                 {
302                         plugin->config.mode = i;
303                         break;
304                 }
305         }
307         plugin->send_configure_change();
308         return 1;
312 OverlayDirection::OverlayDirection(Overlay *plugin,
313         int x, 
314         int y)
315  : BC_PopupMenu(x,
316         y,
317         150,
318         OverlayConfig::direction_to_text(plugin->config.direction),
319         1)
321         this->plugin = plugin;
324 void OverlayDirection::create_objects()
326         add_item(new BC_MenuItem(
327                 OverlayConfig::direction_to_text(
328                         OverlayConfig::TOP_FIRST)));
329         add_item(new BC_MenuItem(
330                 OverlayConfig::direction_to_text(
331                         OverlayConfig::BOTTOM_FIRST)));
334 int OverlayDirection::handle_event()
336         char *text = get_text();
338         if(!strcmp(text, 
339                 OverlayConfig::direction_to_text(
340                         OverlayConfig::TOP_FIRST)))
341                 plugin->config.direction = OverlayConfig::TOP_FIRST;
342         else
343         if(!strcmp(text, 
344                 OverlayConfig::direction_to_text(
345                         OverlayConfig::BOTTOM_FIRST)))
346                 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
348         plugin->send_configure_change();
349         return 1;
353 OverlayOutput::OverlayOutput(Overlay *plugin,
354         int x, 
355         int y)
356  : BC_PopupMenu(x,
357         y,
358         100,
359         OverlayConfig::output_to_text(plugin->config.output_layer),
360         1)
362         this->plugin = plugin;
365 void OverlayOutput::create_objects()
367         add_item(new BC_MenuItem(
368                 OverlayConfig::output_to_text(
369                         OverlayConfig::TOP)));
370         add_item(new BC_MenuItem(
371                 OverlayConfig::output_to_text(
372                         OverlayConfig::BOTTOM)));
375 int OverlayOutput::handle_event()
377         char *text = get_text();
379         if(!strcmp(text, 
380                 OverlayConfig::output_to_text(
381                         OverlayConfig::TOP)))
382                 plugin->config.output_layer = OverlayConfig::TOP;
383         else
384         if(!strcmp(text, 
385                 OverlayConfig::output_to_text(
386                         OverlayConfig::BOTTOM)))
387                 plugin->config.output_layer = OverlayConfig::BOTTOM;
389         plugin->send_configure_change();
390         return 1;
401 PLUGIN_THREAD_OBJECT(Overlay, OverlayThread, OverlayWindow)
412 REGISTER_PLUGIN(Overlay)
419 Overlay::Overlay(PluginServer *server)
420  : PluginVClient(server)
422         PLUGIN_CONSTRUCTOR_MACRO
423         overlayer = 0;
424         temp = 0;
428 Overlay::~Overlay()
430         PLUGIN_DESTRUCTOR_MACRO
431         if(overlayer) delete overlayer;
432         if(temp) delete temp;
437 int Overlay::process_buffer(VFrame **frame,
438         int64_t start_position,
439         double frame_rate)
441         load_configuration();
443         if(!temp) temp = new VFrame(0,
444                 frame[0]->get_w(),
445                 frame[0]->get_h(),
446                 frame[0]->get_color_model(),
447                 -1);
449         if(!overlayer)
450                 overlayer = new OverlayFrame(get_project_smp() + 1);
452         int step;
453         VFrame *output;
455         if(config.direction == OverlayConfig::BOTTOM_FIRST)
456         {
457                 input_layer1 = get_total_buffers() - 1;
458                 input_layer2 = -1;
459                 step = -1;
460         }
461         else
462         {
463                 input_layer1 = 0;
464                 input_layer2 = get_total_buffers();
465                 step = 1;
466         }
468         if(config.output_layer == OverlayConfig::TOP)
469         {
470                 output_layer = 0;
471         }
472         else
473         {
474                 output_layer = get_total_buffers() - 1;
475         }
479 // Direct copy the first layer
480         output = frame[output_layer];
481         read_frame(output, 
482                 input_layer1, 
483                 start_position,
484                 frame_rate,
485                 get_use_opengl());
487         if(get_total_buffers() == 1) return 0;
491         current_layer = input_layer1;
492         if(get_use_opengl()) 
493                 run_opengl();
495         for(int i = input_layer1 + step; i != input_layer2; i += step)
496         {
497                 read_frame(temp, 
498                         i, 
499                         start_position,
500                         frame_rate,
501                         get_use_opengl());
503                 if(get_use_opengl()) 
504                 {
505                         current_layer = i;
506                         run_opengl();
507                 }
508                 else
509 // Call the opengl handler once for each layer
510                         overlayer->overlay(output,
511                                 temp,
512                                 0,
513                                 0,
514                                 output->get_w(),
515                                 output->get_h(),
516                                 0,
517                                 0,
518                                 output->get_w(),
519                                 output->get_h(),
520                                 1,
521                                 config.mode,
522                                 NEAREST_NEIGHBOR);
523         }
526         return 0;
529 int Overlay::handle_opengl()
531 #ifdef HAVE_GL
532         static char *get_pixels_frag = 
533                 "uniform sampler2D src_tex;\n"
534                 "uniform sampler2D dst_tex;\n"
535                 "uniform vec2 dst_tex_dimensions;\n"
536                 "uniform vec3 chroma_offset;\n"
537                 "void main()\n"
538                 "{\n"
539                 "       vec4 result_color;\n"
540                 "       vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
541                 "       vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
542                 "       src_color.rgb -= chroma_offset;\n"
543                 "       dst_color.rgb -= chroma_offset;\n";
545         static char *put_pixels_frag = 
546                 "       result_color.rgb += chroma_offset;\n"
547                 "       result_color.rgb = mix(dst_color.rgb, result_color.rgb, src_color.a);\n"
548                 "       result_color.a = max(src_color.a, dst_color.a);\n"
549                 "       gl_FragColor = result_color;\n"
550                 "}\n";
552         static char *blend_add_frag = 
553                 "       result_color.rgb = dst_color.rgb + src_color.rgb;\n";
555         static char *blend_max_frag = 
556                 "       result_color.r = max(abs(dst_color.r, src_color.r);\n"
557                 "       result_color.g = max(abs(dst_color.g, src_color.g);\n"
558                 "       result_color.b = max(abs(dst_color.b, src_color.b);\n";
560         static char *blend_subtract_frag = 
561                 "       result_color.rgb = dst_color.rgb - src_color.rgb;\n";
564         static char *blend_multiply_frag = 
565                 "       result_color.rgb = dst_color.rgb * src_color.rgb;\n";
567         static char *blend_divide_frag = 
568                 "       result_color.rgb = dst_color.rgb / src_color.rgb;\n"
569                 "       if(src_color.r == 0.0) result_color.r = 1.0;\n"
570                 "       if(src_color.g == 0.0) result_color.g = 1.0;\n"
571                 "       if(src_color.b == 0.0) result_color.b = 1.0;\n";
574         VFrame *src = temp;
575         VFrame *dst = get_output(output_layer);
577         dst->enable_opengl();
578         dst->init_screen();
580         char *shader_stack[] = { 0, 0, 0 };
581         int current_shader = 0;
586 // Direct copy layer
587         if(config.mode == TRANSFER_REPLACE)
588         {
589                 src->to_texture();
590                 src->bind_texture(0);
591                 dst->enable_opengl();
592                 dst->init_screen();
594 // Multiply alpha
595                 glDisable(GL_BLEND);
596                 src->draw_texture();
597         }
598         else
599         if(config.mode == TRANSFER_NORMAL)
600         {
601                 dst->enable_opengl();
602                 dst->init_screen();
604 // Move destination to screen
605                 if(dst->get_opengl_state() != VFrame::SCREEN)
606                 {
607                         dst->to_texture();
608                         dst->bind_texture(0);
609                         dst->draw_texture();
610                 }
612                 src->to_texture();
613                 src->bind_texture(0);
614                 dst->enable_opengl();
615                 dst->init_screen();
617                 glEnable(GL_BLEND);
618                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
619                 src->draw_texture();
620         }
621         else
622         {
623 // Read destination back to texture
624                 dst->to_texture();
626                 src->enable_opengl();
627                 src->init_screen();
628                 src->to_texture();
630                 dst->enable_opengl();
631                 dst->init_screen();
632                 src->bind_texture(0);
633                 dst->bind_texture(1);
636                 shader_stack[current_shader++] = get_pixels_frag;
638                 switch(config.mode)
639                 {
640                         case TRANSFER_ADDITION:
641                                 shader_stack[current_shader++] = blend_add_frag;
642                                 break;
643                         case TRANSFER_SUBTRACT:
644                                 shader_stack[current_shader++] = blend_subtract_frag;
645                                 break;
646                         case TRANSFER_MULTIPLY:
647                                 shader_stack[current_shader++] = blend_multiply_frag;
648                                 break;
649                         case TRANSFER_DIVIDE:
650                                 shader_stack[current_shader++] = blend_divide_frag;
651                                 break;
652                         case TRANSFER_MAX:
653                                 shader_stack[current_shader++] = blend_max_frag;
654                                 break;
655                 }
657                 shader_stack[current_shader++] = put_pixels_frag;
659                 unsigned int shader_id = 0;
660                 shader_id = VFrame::make_shader(0,
661                         shader_stack[0],
662                         shader_stack[1],
663                         shader_stack[2],
664                         0);
666                 glUseProgram(shader_id);
667                 glUniform1i(glGetUniformLocation(shader_id, "src_tex"), 0);
668                 glUniform1i(glGetUniformLocation(shader_id, "dst_tex"), 1);
669                 if(cmodel_is_yuv(dst->get_color_model()))
670                         glUniform3f(glGetUniformLocation(shader_id, "chroma_offset"), 0.0, 0.5, 0.5);
671                 else
672                         glUniform3f(glGetUniformLocation(shader_id, "chroma_offset"), 0.0, 0.0, 0.0);
673                 glUniform2f(glGetUniformLocation(shader_id, "dst_tex_dimensions"), 
674                         (float)dst->get_texture_w(), 
675                         (float)dst->get_texture_h());
677                 glDisable(GL_BLEND);
678                 src->draw_texture();
679                 glUseProgram(0);
680         }
682         glDisable(GL_BLEND);
683         glActiveTexture(GL_TEXTURE1);
684         glDisable(GL_TEXTURE_2D);
685         glActiveTexture(GL_TEXTURE0);
686         glDisable(GL_TEXTURE_2D);
688         dst->set_opengl_state(VFrame::SCREEN);
689 #endif
693 char* Overlay::plugin_title() { return N_("Overlay"); }
694 int Overlay::is_realtime() { return 1; }
695 int Overlay::is_multichannel() { return 1; }
696 int Overlay::is_synthesis() { return 1; }
699 NEW_PICON_MACRO(Overlay) 
701 SHOW_GUI_MACRO(Overlay, OverlayThread)
703 RAISE_WINDOW_MACRO(Overlay)
705 SET_STRING_MACRO(Overlay);
707 int Overlay::load_configuration()
709         KeyFrame *prev_keyframe;
710         prev_keyframe = get_prev_keyframe(get_source_position());
711         read_data(prev_keyframe);
712         return 0;
715 int Overlay::load_defaults()
717         char directory[BCTEXTLEN];
718 // set the default directory
719         sprintf(directory, "%soverlay.rc", BCASTDIR);
721 // load the defaults
722         defaults = new BC_Hash(directory);
723         defaults->load();
725         config.mode = defaults->get("MODE", config.mode);
726         config.direction = defaults->get("DIRECTION", config.direction);
727         config.output_layer = defaults->get("OUTPUT_LAYER", config.output_layer);
728         return 0;
731 int Overlay::save_defaults()
733         defaults->update("MODE", config.mode);
734         defaults->update("DIRECTION", config.direction);
735         defaults->update("OUTPUT_LAYER", config.output_layer);
736         defaults->save();
737         return 0;
740 void Overlay::save_data(KeyFrame *keyframe)
742         FileXML output;
744 // cause data to be stored directly in text
745         output.set_shared_string(keyframe->data, MESSAGESIZE);
746         output.tag.set_title("OVERLAY");
747         output.tag.set_property("MODE", config.mode);
748         output.tag.set_property("DIRECTION", config.direction);
749         output.tag.set_property("OUTPUT_LAYER", config.output_layer);
750         output.append_tag();
751         output.tag.set_title("/OVERLAY");
752         output.append_tag();
753         output.terminate_string();
756 void Overlay::read_data(KeyFrame *keyframe)
758         FileXML input;
760         input.set_shared_string(keyframe->data, strlen(keyframe->data));
762         int result = 0;
764         while(!input.read_tag())
765         {
766                 if(input.tag.title_is("OVERLAY"))
767                 {
768                         config.mode = input.tag.get_property("MODE", config.mode);
769                         config.direction = input.tag.get_property("DIRECTION", config.direction);
770                         config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
771                 }
772         }
775 void Overlay::update_gui()
777         if(thread)
778         {
779                 thread->window->lock_window("Overlay::update_gui");
780                 thread->window->mode->set_text(OverlayConfig::mode_to_text(config.mode));
781                 thread->window->direction->set_text(OverlayConfig::direction_to_text(config.direction));
782                 thread->window->output->set_text(OverlayConfig::output_to_text(config.output_layer));
783                 thread->window->unlock_window();
784         }