initial commit
[goodguy/cinelerra.git] / cinelerra-5.1 / 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 "edlsession.h"
26 #include "filexml.h"
27 #include "guicast.h"
28 #include "keyframe.h"
29 #include "language.h"
30 #include "overlayframe.h"
31 #include "pluginvclient.h"
32 #include "vpatchgui.h"
33 #include "vframe.h"
34
35 #include <string.h>
36 #include <stdint.h>
37
38
39 class Overlay;
40 class OverlayWindow;
41
42
43 class OverlayConfig
44 {
45 public:
46         OverlayConfig();
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 class OverlayMode : public BC_PopupMenu
69 {
70 public:
71         OverlayMode(Overlay *plugin,
72                 int x,
73                 int y);
74         void create_objects();
75         int handle_event();
76         Overlay *plugin;
77 };
78
79 class OverlayDirection : public BC_PopupMenu
80 {
81 public:
82         OverlayDirection(Overlay *plugin,
83                 int x,
84                 int y);
85         void create_objects();
86         int handle_event();
87         Overlay *plugin;
88 };
89
90 class OverlayOutput : public BC_PopupMenu
91 {
92 public:
93         OverlayOutput(Overlay *plugin,
94                 int x,
95                 int y);
96         void create_objects();
97         int handle_event();
98         Overlay *plugin;
99 };
100
101
102 class OverlayWindow : public PluginClientWindow
103 {
104 public:
105         OverlayWindow(Overlay *plugin);
106         ~OverlayWindow();
107
108         void create_objects();
109
110
111         Overlay *plugin;
112         OverlayMode *mode;
113         OverlayDirection *direction;
114         OverlayOutput *output;
115 };
116
117 class Overlay : public PluginVClient
118 {
119 public:
120         Overlay(PluginServer *server);
121         ~Overlay();
122
123
124         PLUGIN_CLASS_MEMBERS(OverlayConfig);
125
126         int process_buffer(VFrame **frame,
127                 int64_t start_position,
128                 double frame_rate);
129         int is_realtime();
130         int is_multichannel();
131         int is_synthesis();
132         void save_data(KeyFrame *keyframe);
133         void read_data(KeyFrame *keyframe);
134         void update_gui();
135         int handle_opengl();
136
137         OverlayFrame *overlayer;
138         VFrame *temp;
139         int current_layer;
140         int output_layer;
141         int input_layer;
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         return VModePatch::mode_to_text(mode);
154 }
155
156 const char* OverlayConfig::direction_to_text(int direction)
157 {
158         switch(direction)
159         {
160                 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
161                 case OverlayConfig::TOP_FIRST:    return _("Top first");
162         }
163         return "";
164 }
165
166 const char* OverlayConfig::output_to_text(int output_layer)
167 {
168         switch(output_layer)
169         {
170                 case OverlayConfig::TOP:    return _("Top");
171                 case OverlayConfig::BOTTOM: return _("Bottom");
172         }
173         return "";
174 }
175
176
177
178
179
180
181
182
183
184 OverlayWindow::OverlayWindow(Overlay *plugin)
185  : PluginClientWindow(plugin,
186         300,
187         160,
188         300,
189         160,
190         0)
191 {
192         this->plugin = plugin;
193 }
194
195 OverlayWindow::~OverlayWindow()
196 {
197 }
198
199 void OverlayWindow::create_objects()
200 {
201         int x = 10, y = 10;
202
203         BC_Title *title;
204         add_subwindow(title = new BC_Title(x, y, _("Mode:")));
205         add_subwindow(mode = new OverlayMode(plugin,
206                 x + title->get_w() + 5,
207                 y));
208         mode->create_objects();
209
210         y += 30;
211         add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
212         add_subwindow(direction = new OverlayDirection(plugin,
213                 x + title->get_w() + 5,
214                 y));
215         direction->create_objects();
216
217         y += 30;
218         add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
219         add_subwindow(output = new OverlayOutput(plugin,
220                 x + title->get_w() + 5,
221                 y));
222         output->create_objects();
223
224         show_window();
225         flush();
226 }
227
228
229
230
231
232
233
234 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
235  : BC_PopupMenu(x, y, 150,
236         OverlayConfig::mode_to_text(plugin->config.mode), 1)
237 {
238         this->plugin = plugin;
239 }
240
241 void OverlayMode::create_objects()
242 {
243         for(int i = 0; i < TRANSFER_TYPES; i++)
244                 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
245 }
246
247 int OverlayMode::handle_event()
248 {
249         char *text = get_text();
250
251         for(int i = 0; i < TRANSFER_TYPES; i++)
252         {
253                 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
254                 {
255                         plugin->config.mode = i;
256                         break;
257                 }
258         }
259
260         plugin->send_configure_change();
261         return 1;
262 }
263
264
265 OverlayDirection::OverlayDirection(Overlay *plugin,
266         int x,
267         int y)
268  : BC_PopupMenu(x,
269         y,
270         150,
271         OverlayConfig::direction_to_text(plugin->config.direction),
272         1)
273 {
274         this->plugin = plugin;
275 }
276
277 void OverlayDirection::create_objects()
278 {
279         add_item(new BC_MenuItem(
280                 OverlayConfig::direction_to_text(
281                         OverlayConfig::TOP_FIRST)));
282         add_item(new BC_MenuItem(
283                 OverlayConfig::direction_to_text(
284                         OverlayConfig::BOTTOM_FIRST)));
285 }
286
287 int OverlayDirection::handle_event()
288 {
289         char *text = get_text();
290
291         if(!strcmp(text,
292                 OverlayConfig::direction_to_text(
293                         OverlayConfig::TOP_FIRST)))
294                 plugin->config.direction = OverlayConfig::TOP_FIRST;
295         else
296         if(!strcmp(text,
297                 OverlayConfig::direction_to_text(
298                         OverlayConfig::BOTTOM_FIRST)))
299                 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
300
301         plugin->send_configure_change();
302         return 1;
303 }
304
305
306 OverlayOutput::OverlayOutput(Overlay *plugin,
307         int x,
308         int y)
309  : BC_PopupMenu(x,
310         y,
311         100,
312         OverlayConfig::output_to_text(plugin->config.output_layer),
313         1)
314 {
315         this->plugin = plugin;
316 }
317
318 void OverlayOutput::create_objects()
319 {
320         add_item(new BC_MenuItem(
321                 OverlayConfig::output_to_text(
322                         OverlayConfig::TOP)));
323         add_item(new BC_MenuItem(
324                 OverlayConfig::output_to_text(
325                         OverlayConfig::BOTTOM)));
326 }
327
328 int OverlayOutput::handle_event()
329 {
330         char *text = get_text();
331
332         if(!strcmp(text,
333                 OverlayConfig::output_to_text(
334                         OverlayConfig::TOP)))
335                 plugin->config.output_layer = OverlayConfig::TOP;
336         else
337         if(!strcmp(text,
338                 OverlayConfig::output_to_text(
339                         OverlayConfig::BOTTOM)))
340                 plugin->config.output_layer = OverlayConfig::BOTTOM;
341
342         plugin->send_configure_change();
343         return 1;
344 }
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365 REGISTER_PLUGIN(Overlay)
366
367
368
369
370
371
372 Overlay::Overlay(PluginServer *server)
373  : PluginVClient(server)
374 {
375
376         overlayer = 0;
377         temp = 0;
378 }
379
380
381 Overlay::~Overlay()
382 {
383
384         if(overlayer) delete overlayer;
385         if(temp) delete temp;
386 }
387
388
389
390 int Overlay::process_buffer(VFrame **frame,
391         int64_t start_position,
392         double frame_rate)
393 {
394         load_configuration();
395
396         EDLSession* session = get_edlsession();
397         int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
398
399         int step = config.direction == OverlayConfig::BOTTOM_FIRST ?  -1 : 1;
400         int layers = get_total_buffers();
401         input_layer = config.direction == OverlayConfig::BOTTOM_FIRST ? layers-1 : 0;
402         output_layer = config.output_layer == OverlayConfig::TOP ?  0 : layers-1;
403         VFrame *output = frame[output_layer];
404
405         current_layer = input_layer;
406         read_frame(output, current_layer,  // Direct copy the first layer
407                          start_position, frame_rate, get_use_opengl());
408
409         if( --layers > 0 ) {    // need 2 layers to do overlay
410                 if( !temp )
411                         temp = new VFrame(frame[0]->get_w(), frame[0]->get_h(),
412                                         frame[0]->get_color_model(), 0);
413
414                 while( --layers >= 0 ) {
415                         current_layer += step;
416                         read_frame(temp, current_layer,
417                                  start_position, frame_rate, get_use_opengl());
418
419                         if(get_use_opengl()) {
420                                 run_opengl();
421                                 continue;
422                         }
423
424                         if(!overlayer)
425                                 overlayer = new OverlayFrame(get_project_smp() + 1);
426
427                         overlayer->overlay(output, temp,
428                                 0, 0, output->get_w(), output->get_h(),
429                                 0, 0, output->get_w(), output->get_h(),
430                                 1, config.mode, interpolation_type);
431                 }
432         }
433
434         return 0;
435 }
436
437 int Overlay::handle_opengl()
438 {
439 #ifdef HAVE_GL
440         static const char *get_pixels_frag =
441                 "uniform sampler2D src_tex;\n"
442                 "uniform sampler2D dst_tex;\n"
443                 "uniform vec2 dst_tex_dimensions;\n"
444                 "uniform vec3 chroma_offset;\n"
445                 "void main()\n"
446                 "{\n"
447                 "       vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
448                 "       vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
449                 "       src_color.rgb -= chroma_offset;\n"
450                 "       dst_color.rgb -= chroma_offset;\n";
451
452         static const char *put_pixels_frag =
453                 "       result.rgb += chroma_offset;\n"
454                 "       gl_FragColor = result;\n"
455                 "}\n";
456
457 #define QQ(q)#q
458 #define SS(s)QQ(s)
459
460 #define GL_STD_FRAG(FN) static const char *blend_##FN##_frag = \
461         "       vec4 result;\n" \
462         "       result.rgb = " SS(COLOR_##FN(1.0, src_color.rgb, src_color.a, dst_color.rgb, dst_color.a)) ";\n" \
463         "       result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a))";\n" \
464
465 #define GL_VEC_FRAG(FN) static const char *blend_##FN##_frag = \
466         "       vec4 result;\n" \
467         "       result.r = " SS(COLOR_##FN(1.0, src_color.r, src_color.a, dst_color.r, dst_color.a)) ";\n" \
468         "       result.g = " SS(COLOR_##FN(1.0, src_color.g, src_color.a, dst_color.g, dst_color.a)) ";\n" \
469         "       result.b = " SS(COLOR_##FN(1.0, src_color.b, src_color.a, dst_color.b, dst_color.a)) ";\n" \
470         "       result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a)) ";\n" \
471         "       result = clamp(result, 0.0, 1.0);\n" \
472
473 #undef mabs
474 #define mabs abs
475 #undef mmin
476 #define mmin min
477 #undef mmax
478 #define mmax max
479
480 #undef ZERO
481 #define ZERO 0.0
482 #undef ONE
483 #define ONE 1.0
484 #undef TWO
485 #define TWO 2.0
486
487 static const char *blend_NORMAL_frag =
488         "       vec4 result = mix(src_color, src_color, src_color.a);\n";
489
490 static const char *blend_ADDITION_frag =
491         "       vec4 result = dst_color + src_color;\n"
492         "       result = clamp(result, 0.0, 1.0);\n";
493
494 static const char *blend_SUBTRACT_frag =
495         "       vec4 result = dst_color - src_color;\n"
496         "       result = clamp(result, 0.0, 1.0);\n";
497
498 static const char *blend_REPLACE_frag =
499         "       vec4 result = src_color;\n";
500
501 GL_STD_FRAG(MULTIPLY);
502 GL_VEC_FRAG(DIVIDE);
503 GL_VEC_FRAG(MAX);
504 GL_VEC_FRAG(MIN);
505 GL_VEC_FRAG(DARKEN);
506 GL_VEC_FRAG(LIGHTEN);
507 GL_STD_FRAG(DST);
508 GL_STD_FRAG(DST_ATOP);
509 GL_STD_FRAG(DST_IN);
510 GL_STD_FRAG(DST_OUT);
511 GL_STD_FRAG(DST_OVER);
512 GL_STD_FRAG(SRC);
513 GL_STD_FRAG(SRC_ATOP);
514 GL_STD_FRAG(SRC_IN);
515 GL_STD_FRAG(SRC_OUT);
516 GL_STD_FRAG(SRC_OVER);
517 GL_STD_FRAG(AND);
518 GL_STD_FRAG(OR);
519 GL_STD_FRAG(XOR);
520 GL_VEC_FRAG(OVERLAY);
521 GL_STD_FRAG(SCREEN);
522 GL_VEC_FRAG(BURN);
523 GL_VEC_FRAG(DODGE);
524 GL_VEC_FRAG(HARDLIGHT);
525 GL_VEC_FRAG(SOFTLIGHT);
526 GL_VEC_FRAG(DIFFERENCE);
527
528 static const char * const overlay_shaders[TRANSFER_TYPES] = {
529         blend_NORMAL_frag,      // TRANSFER_NORMAL
530         blend_ADDITION_frag,    // TRANSFER_ADDITION
531         blend_SUBTRACT_frag,    // TRANSFER_SUBTRACT
532         blend_MULTIPLY_frag,    // TRANSFER_MULTIPLY
533         blend_DIVIDE_frag,      // TRANSFER_DIVIDE
534         blend_REPLACE_frag,     // TRANSFER_REPLACE
535         blend_MAX_frag,         // TRANSFER_MAX
536         blend_MIN_frag,         // TRANSFER_MIN
537         blend_DARKEN_frag,      // TRANSFER_DARKEN
538         blend_LIGHTEN_frag,     // TRANSFER_LIGHTEN
539         blend_DST_frag,         // TRANSFER_DST
540         blend_DST_ATOP_frag,    // TRANSFER_DST_ATOP
541         blend_DST_IN_frag,      // TRANSFER_DST_IN
542         blend_DST_OUT_frag,     // TRANSFER_DST_OUT
543         blend_DST_OVER_frag,    // TRANSFER_DST_OVER
544         blend_SRC_frag,         // TRANSFER_SRC
545         blend_SRC_ATOP_frag,    // TRANSFER_SRC_ATOP
546         blend_SRC_IN_frag,      // TRANSFER_SRC_IN
547         blend_SRC_OUT_frag,     // TRANSFER_SRC_OUT
548         blend_SRC_OVER_frag,    // TRANSFER_SRC_OVER
549         blend_AND_frag,         // TRANSFER_AND
550         blend_OR_frag,          // TRANSFER_OR
551         blend_XOR_frag,         // TRANSFER_XOR
552         blend_OVERLAY_frag,     // TRANSFER_OVERLAY
553         blend_SCREEN_frag,      // TRANSFER_SCREEN
554         blend_BURN_frag,        // TRANSFER_BURN
555         blend_DODGE_frag,       // TRANSFER_DODGE
556         blend_HARDLIGHT_frag,   // TRANSFER_HARDLIGHT
557         blend_SOFTLIGHT_frag,   // TRANSFER_SOFTLIGHT
558         blend_DIFFERENCE_frag,  // TRANSFER_DIFFERENCE
559 };
560
561         glDisable(GL_BLEND);
562         VFrame *dst = get_output(output_layer);
563         VFrame *src = temp;
564
565         switch( config.mode ) {
566         case TRANSFER_REPLACE:
567         case TRANSFER_SRC:
568 // Direct copy layer
569                 src->to_texture();
570                 dst->enable_opengl();
571                 dst->init_screen();
572                 src->draw_texture();
573                 break;
574         case TRANSFER_NORMAL:
575 // Move destination to screen
576                 if( dst->get_opengl_state() != VFrame::SCREEN ) {
577                         dst->to_texture();
578                         dst->enable_opengl();
579                         dst->init_screen();
580                         dst->draw_texture();
581                 }
582                 src->to_texture();
583                 dst->enable_opengl();
584                 dst->init_screen();
585                 glEnable(GL_BLEND);
586                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
587                 src->draw_texture();
588                 break;
589         default:
590                 src->to_texture();
591                 dst->to_texture();
592                 dst->enable_opengl();
593                 dst->init_screen();
594                 src->bind_texture(0);
595                 dst->bind_texture(1);
596
597                 const char *shader_stack[16];
598                 memset(shader_stack,0, sizeof(shader_stack));
599                 int current_shader = 0;
600
601                 shader_stack[current_shader++] = get_pixels_frag;
602                 shader_stack[current_shader++] = overlay_shaders[config.mode];
603                 shader_stack[current_shader++] = put_pixels_frag;
604                 shader_stack[current_shader] = 0;
605                 unsigned int shader = VFrame::make_shader(shader_stack);
606                 if( shader > 0 ) {
607                         glUseProgram(shader);
608                         glUniform1i(glGetUniformLocation(shader, "src_tex"), 0);
609                         glUniform1i(glGetUniformLocation(shader, "dst_tex"), 1);
610                         glUniform2f(glGetUniformLocation(shader, "dst_tex_dimensions"),
611                                         (float)dst->get_texture_w(), (float)dst->get_texture_h());
612                         float chroma_offset = BC_CModels::is_yuv(dst->get_color_model()) ? 0.5 : 0.0;
613                         glUniform3f(glGetUniformLocation(shader, "chroma_offset"),
614                                         0.0, chroma_offset, chroma_offset);
615                 }
616                 src->draw_texture();
617                 glUseProgram(0);
618
619                 glActiveTexture(GL_TEXTURE1);
620                 glDisable(GL_TEXTURE_2D);
621                 break;
622         }
623
624         glActiveTexture(GL_TEXTURE0);
625         glDisable(GL_TEXTURE_2D);
626         glDisable(GL_BLEND);
627
628 // get the data before something else uses the screen
629         dst->screen_to_ram();
630 #endif
631         return 0;
632 }
633
634
635 const char* Overlay::plugin_title() { return N_("Overlay"); }
636 int Overlay::is_realtime() { return 1; }
637 int Overlay::is_multichannel() { return 1; }
638 int Overlay::is_synthesis() { return 1; }
639
640
641
642 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
643
644
645
646 int Overlay::load_configuration()
647 {
648         KeyFrame *prev_keyframe;
649         prev_keyframe = get_prev_keyframe(get_source_position());
650         read_data(prev_keyframe);
651         return 0;
652 }
653
654
655 void Overlay::save_data(KeyFrame *keyframe)
656 {
657         FileXML output;
658
659 // cause data to be stored directly in text
660         output.set_shared_output(keyframe->xbuf);
661         output.tag.set_title("OVERLAY");
662         output.tag.set_property("MODE", config.mode);
663         output.tag.set_property("DIRECTION", config.direction);
664         output.tag.set_property("OUTPUT_LAYER", config.output_layer);
665         output.append_tag();
666         output.tag.set_title("/OVERLAY");
667         output.append_tag();
668         output.terminate_string();
669 }
670
671 void Overlay::read_data(KeyFrame *keyframe)
672 {
673         FileXML input;
674
675         input.set_shared_input(keyframe->xbuf);
676
677         while(!input.read_tag())
678         {
679                 if(input.tag.title_is("OVERLAY"))
680                 {
681                         config.mode = input.tag.get_property("MODE", config.mode);
682                         config.direction = input.tag.get_property("DIRECTION", config.direction);
683                         config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
684                 }
685         }
686 }
687
688 void Overlay::update_gui()
689 {
690         if(thread)
691         {
692                 thread->window->lock_window("Overlay::update_gui");
693                 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
694                 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
695                 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
696                 thread->window->unlock_window();
697         }
698 }
699
700
701
702
703