4 * Copyright (C) 2008 Adam Williams <broadcast at earthling dot net>
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.
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.
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
22 #include "bcdisplayinfo.h"
25 #include "edlsession.h"
30 #include "overlayframe.h"
31 #include "pluginvclient.h"
47 static const char* mode_to_text(int mode);
50 static const char* direction_to_text(int direction);
58 static const char* output_to_text(int output_layer);
67 class OverlayMode : public BC_PopupMenu
70 OverlayMode(Overlay *plugin,
73 void create_objects();
78 class OverlayDirection : public BC_PopupMenu
81 OverlayDirection(Overlay *plugin,
84 void create_objects();
89 class OverlayOutput : public BC_PopupMenu
92 OverlayOutput(Overlay *plugin,
95 void create_objects();
101 class OverlayWindow : public PluginClientWindow
104 OverlayWindow(Overlay *plugin);
107 void create_objects();
112 OverlayDirection *direction;
113 OverlayOutput *output;
116 class Overlay : public PluginVClient
119 Overlay(PluginServer *server);
123 PLUGIN_CLASS_MEMBERS(OverlayConfig);
125 int process_buffer(VFrame **frame,
126 int64_t start_position,
129 int is_multichannel();
131 void save_data(KeyFrame *keyframe);
132 void read_data(KeyFrame *keyframe);
136 OverlayFrame *overlayer;
143 OverlayConfig::OverlayConfig()
145 mode = TRANSFER_NORMAL;
146 direction = OverlayConfig::BOTTOM_FIRST;
147 output_layer = OverlayConfig::TOP;
150 const char* OverlayConfig::mode_to_text(int mode)
153 case TRANSFER_NORMAL: return _("Normal");
154 case TRANSFER_ADDITION: return _("Addition");
155 case TRANSFER_SUBTRACT: return _("Subtract");
156 case TRANSFER_MULTIPLY: return _("Multiply");
157 case TRANSFER_DIVIDE: return _("Divide");
158 case TRANSFER_REPLACE: return _("Replace");
159 case TRANSFER_MAX: return _("Max");
160 case TRANSFER_MIN: return _("Min");
161 case TRANSFER_AVERAGE: return _("Average");
162 case TRANSFER_DARKEN: return _("Darken");
163 case TRANSFER_LIGHTEN: return _("Lighten");
164 case TRANSFER_DST: return _("Dst");
165 case TRANSFER_DST_ATOP: return _("DstAtop");
166 case TRANSFER_DST_IN: return _("DstIn");
167 case TRANSFER_DST_OUT: return _("DstOut");
168 case TRANSFER_DST_OVER: return _("DstOver");
169 case TRANSFER_SRC: return _("Src");
170 case TRANSFER_SRC_ATOP: return _("SrcAtop");
171 case TRANSFER_SRC_IN: return _("SrcIn");
172 case TRANSFER_SRC_OUT: return _("SrcOut");
173 case TRANSFER_SRC_OVER: return _("SrcOver");
174 case TRANSFER_OR: return _("Or");
175 case TRANSFER_XOR: return _("Xor");
181 const char* OverlayConfig::direction_to_text(int direction)
185 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
186 case OverlayConfig::TOP_FIRST: return _("Top first");
191 const char* OverlayConfig::output_to_text(int output_layer)
195 case OverlayConfig::TOP: return _("Top");
196 case OverlayConfig::BOTTOM: return _("Bottom");
209 OverlayWindow::OverlayWindow(Overlay *plugin)
210 : PluginClientWindow(plugin,
217 this->plugin = plugin;
220 OverlayWindow::~OverlayWindow()
224 void OverlayWindow::create_objects()
229 add_subwindow(title = new BC_Title(x, y, _("Mode:")));
230 add_subwindow(mode = new OverlayMode(plugin,
231 x + title->get_w() + 5,
233 mode->create_objects();
236 add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
237 add_subwindow(direction = new OverlayDirection(plugin,
238 x + title->get_w() + 5,
240 direction->create_objects();
243 add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
244 add_subwindow(output = new OverlayOutput(plugin,
245 x + title->get_w() + 5,
247 output->create_objects();
259 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
260 : BC_PopupMenu(x, y, 150,
261 OverlayConfig::mode_to_text(plugin->config.mode), 1)
263 this->plugin = plugin;
266 void OverlayMode::create_objects()
268 for(int i = 0; i < TRANSFER_TYPES; i++)
269 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
272 int OverlayMode::handle_event()
274 char *text = get_text();
276 for(int i = 0; i < TRANSFER_TYPES; i++)
278 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
280 plugin->config.mode = i;
285 plugin->send_configure_change();
290 OverlayDirection::OverlayDirection(Overlay *plugin,
296 OverlayConfig::direction_to_text(plugin->config.direction),
299 this->plugin = plugin;
302 void OverlayDirection::create_objects()
304 add_item(new BC_MenuItem(
305 OverlayConfig::direction_to_text(
306 OverlayConfig::TOP_FIRST)));
307 add_item(new BC_MenuItem(
308 OverlayConfig::direction_to_text(
309 OverlayConfig::BOTTOM_FIRST)));
312 int OverlayDirection::handle_event()
314 char *text = get_text();
317 OverlayConfig::direction_to_text(
318 OverlayConfig::TOP_FIRST)))
319 plugin->config.direction = OverlayConfig::TOP_FIRST;
322 OverlayConfig::direction_to_text(
323 OverlayConfig::BOTTOM_FIRST)))
324 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
326 plugin->send_configure_change();
331 OverlayOutput::OverlayOutput(Overlay *plugin,
337 OverlayConfig::output_to_text(plugin->config.output_layer),
340 this->plugin = plugin;
343 void OverlayOutput::create_objects()
345 add_item(new BC_MenuItem(
346 OverlayConfig::output_to_text(
347 OverlayConfig::TOP)));
348 add_item(new BC_MenuItem(
349 OverlayConfig::output_to_text(
350 OverlayConfig::BOTTOM)));
353 int OverlayOutput::handle_event()
355 char *text = get_text();
358 OverlayConfig::output_to_text(
359 OverlayConfig::TOP)))
360 plugin->config.output_layer = OverlayConfig::TOP;
363 OverlayConfig::output_to_text(
364 OverlayConfig::BOTTOM)))
365 plugin->config.output_layer = OverlayConfig::BOTTOM;
367 plugin->send_configure_change();
390 REGISTER_PLUGIN(Overlay)
397 Overlay::Overlay(PluginServer *server)
398 : PluginVClient(server)
409 if(overlayer) delete overlayer;
410 if(temp) delete temp;
415 int Overlay::process_buffer(VFrame **frame,
416 int64_t start_position,
419 load_configuration();
421 EDLSession* session = get_edlsession();
422 int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
424 int step = config.direction == OverlayConfig::BOTTOM_FIRST ? -1 : 1;
425 int layers = get_total_buffers();
426 input_layer = config.direction == OverlayConfig::BOTTOM_FIRST ? layers-1 : 0;
427 output_layer = config.output_layer == OverlayConfig::TOP ? 0 : layers-1;
428 VFrame *output = frame[output_layer];
430 current_layer = input_layer;
431 read_frame(output, current_layer, // Direct copy the first layer
432 start_position, frame_rate, get_use_opengl());
434 if( --layers > 0 ) { // need 2 layers to do overlay
436 temp = new VFrame(0, -1, frame[0]->get_w(), frame[0]->get_h(),
437 frame[0]->get_color_model(), -1);
439 while( --layers >= 0 ) {
440 current_layer += step;
441 read_frame(temp, current_layer,
442 start_position, frame_rate, get_use_opengl());
444 if(get_use_opengl()) {
450 overlayer = new OverlayFrame(get_project_smp() + 1);
452 overlayer->overlay(output, temp,
453 0, 0, output->get_w(), output->get_h(),
454 0, 0, output->get_w(), output->get_h(),
455 1, config.mode, interpolation_type);
462 int Overlay::handle_opengl()
465 static const char *get_pixels_frag =
466 "uniform sampler2D src_tex;\n"
467 "uniform sampler2D dst_tex;\n"
468 "uniform vec2 dst_tex_dimensions;\n"
469 "uniform vec3 chroma_offset;\n"
472 " vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
473 " vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
474 " src_color.rgb -= chroma_offset;\n"
475 " dst_color.rgb -= chroma_offset;\n";
477 static const char *put_pixels_frag =
478 " result.rgb += chroma_offset;\n"
479 " gl_FragColor = result;\n"
484 static const char *blend_normal_frag =
485 " vec4 result = mix(src_color, src_color, src_color.a);\n";
488 static const char *blend_add_frag =
489 " vec4 result = dst_color + src_color;\n"
490 " result = clamp(result, 0.0, 1.0);\n";
493 static const char *blend_subtract_frag =
494 " vec4 result = dst_color - src_color;\n"
495 " result = clamp(result, 0.0, 1.0);\n";
498 static const char *blend_multiply_frag =
499 " vec4 result = dst_color * src_color;\n";
502 static const char *blend_divide_frag =
503 " vec4 result = dst_color / src_color;\n"
504 " if(src_color.r == 0.) result.r = 1.0;\n"
505 " if(src_color.g == 0.) result.g = 1.0;\n"
506 " if(src_color.b == 0.) result.b = 1.0;\n"
507 " if(src_color.a == 0.) result.a = 1.0;\n"
508 " result = clamp(result, 0.0, 1.0);\n";
511 static const char *blend_max_frag =
512 " vec4 result = max(src_color, dst_color);\n";
515 static const char *blend_min_frag =
516 " vec4 result = min(src_color, dst_color);\n";
519 static const char *blend_average_frag =
520 " vec4 result = (src_color + dst_color) * 0.5;\n";
523 static const char *blend_darken_frag =
524 " vec4 result = vec4(src_color.rgb * (1.0 - dst_color.a) +"
525 " dst_color.rgb * (1.0 - src_color.a) +"
526 " min(src_color.rgb, dst_color.rgb), "
527 " src_color.a + dst_color.a - src_color.a * dst_color.a);\n"
528 " result = clamp(result, 0.0, 1.0);\n";
531 static const char *blend_lighten_frag =
532 " vec4 result = vec4(src_color.rgb * (1.0 - dst_color.a) +"
533 " dst_color.rgb * (1.0 - src_color.a) +"
534 " max(src_color.rgb, dst_color.rgb), "
535 " src_color.a + dst_color.a - src_color.a * dst_color.a);\n"
536 " result = clamp(result, 0.0, 1.0);\n";
539 static const char *blend_dst_frag =
540 " vec4 result = dst_color;\n";
543 static const char *blend_dst_atop_frag =
544 " vec4 result = vec4(src_color.rgb * dst_color.a + "
545 "(1.0 - src_color.a) * dst_color.rgb, dst_color.a);\n";
548 static const char *blend_dst_in_frag =
549 " vec4 result = src_color * dst_color.a;\n";
552 static const char *blend_dst_out_frag =
553 " vec4 result = src_color * (1.0 - dst_color.a);\n";
556 static const char *blend_dst_over_frag =
557 " vec4 result = vec4(src_color.rgb + (1.0 - src_color.a) * dst_color.rgb, "
558 " dst_color.a + src_color.a - dst_color.a * src_color.a);\n";
561 static const char *blend_src_frag =
562 " vec4 result = src_color;\n";
565 static const char *blend_src_atop_frag =
566 " vec4 result = vec4(dst_color.rgb * src_color.a + "
567 "src_color.rgb * (1.0 - dst_color.a), src_color.a);\n";
570 static const char *blend_src_in_frag =
571 " vec4 result = dst_color * src_color.a;\n";
574 static const char *blend_src_out_frag =
575 " vec4 result = dst_color * (1.0 - src_color.a);\n";
578 static const char *blend_src_over_frag =
579 " vec4 result = vec4(dst_color.rgb + (1.0 - dst_color.a) * src_color.rgb, "
580 "dst_color.a + src_color.a - dst_color.a * src_color.a);\n";
583 static const char *blend_or_frag =
584 " vec4 result = src_color + dst_color - src_color * dst_color;\n";
587 static const char *blend_xor_frag =
588 " vec4 result = vec4(dst_color.rgb * (1.0 - src_color.a) + "
589 "(1.0 - dst_color.a) * src_color.rgb, "
590 "dst_color.a + src_color.a - 2.0 * dst_color.a * src_color.a);\n";
592 static const char * const overlay_shaders[TRANSFER_TYPES] = {
593 blend_normal_frag, // TRANSFER_NORMAL
594 blend_add_frag, // TRANSFER_ADDITION
595 blend_subtract_frag, // TRANSFER_SUBTRACT
596 blend_multiply_frag, // TRANSFER_MULTIPLY
597 blend_divide_frag, // TRANSFER_DIVIDE
598 blend_src_frag, // TRANSFER_REPLACE
599 blend_max_frag, // TRANSFER_MAX
600 blend_min_frag, // TRANSFER_MIN
601 blend_average_frag, // TRANSFER_AVERAGE
602 blend_darken_frag, // TRANSFER_DARKEN
603 blend_lighten_frag, // TRANSFER_LIGHTEN
604 blend_dst_frag, // TRANSFER_DST
605 blend_dst_atop_frag, // TRANSFER_DST_ATOP
606 blend_dst_in_frag, // TRANSFER_DST_IN
607 blend_dst_out_frag, // TRANSFER_DST_OUT
608 blend_dst_over_frag, // TRANSFER_DST_OVER
609 blend_src_frag, // TRANSFER_SRC
610 blend_src_atop_frag, // TRANSFER_SRC_ATOP
611 blend_src_in_frag, // TRANSFER_SRC_IN
612 blend_src_out_frag, // TRANSFER_SRC_OUT
613 blend_src_over_frag, // TRANSFER_SRC_OVER
614 blend_or_frag, // TRANSFER_OR
615 blend_xor_frag // TRANSFER_XOR
619 VFrame *dst = get_output(output_layer);
622 switch( config.mode ) {
623 case TRANSFER_REPLACE:
627 dst->enable_opengl();
631 case TRANSFER_NORMAL:
632 // Move destination to screen
633 if( dst->get_opengl_state() != VFrame::SCREEN ) {
635 dst->enable_opengl();
640 dst->enable_opengl();
643 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
649 dst->enable_opengl();
651 src->bind_texture(0);
652 dst->bind_texture(1);
653 const char *shader_stack[] = { 0, 0, 0 };
654 int current_shader = 0;
656 shader_stack[current_shader++] = get_pixels_frag;
657 shader_stack[current_shader++] = overlay_shaders[config.mode];
658 shader_stack[current_shader++] = put_pixels_frag;
660 unsigned int shader_id = 0;
661 shader_id = VFrame::make_shader(0,
667 glUseProgram(shader_id);
668 glUniform1i(glGetUniformLocation(shader_id, "src_tex"), 0);
669 glUniform1i(glGetUniformLocation(shader_id, "dst_tex"), 1);
670 glUniform2f(glGetUniformLocation(shader_id, "dst_tex_dimensions"),
671 (float)dst->get_texture_w(), (float)dst->get_texture_h());
672 float chroma_offset = BC_CModels::is_yuv(dst->get_color_model()) ? 0.5 : 0.0;
673 glUniform3f(glGetUniformLocation(shader_id, "chroma_offset"),
674 0.0, chroma_offset, chroma_offset);
679 glActiveTexture(GL_TEXTURE1);
680 glDisable(GL_TEXTURE_2D);
684 glActiveTexture(GL_TEXTURE0);
685 glDisable(GL_TEXTURE_2D);
688 // get the data before something else uses the screen
689 dst->screen_to_ram();
695 const char* Overlay::plugin_title() { return _("Overlay"); }
696 int Overlay::is_realtime() { return 1; }
697 int Overlay::is_multichannel() { return 1; }
698 int Overlay::is_synthesis() { return 1; }
702 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
706 int Overlay::load_configuration()
708 KeyFrame *prev_keyframe;
709 prev_keyframe = get_prev_keyframe(get_source_position());
710 read_data(prev_keyframe);
715 void Overlay::save_data(KeyFrame *keyframe)
719 // cause data to be stored directly in text
720 output.set_shared_output(keyframe->get_data(), MESSAGESIZE);
721 output.tag.set_title("OVERLAY");
722 output.tag.set_property("MODE", config.mode);
723 output.tag.set_property("DIRECTION", config.direction);
724 output.tag.set_property("OUTPUT_LAYER", config.output_layer);
726 output.tag.set_title("/OVERLAY");
728 output.terminate_string();
731 void Overlay::read_data(KeyFrame *keyframe)
735 input.set_shared_input(keyframe->get_data(), strlen(keyframe->get_data()));
737 while(!input.read_tag())
739 if(input.tag.title_is("OVERLAY"))
741 config.mode = input.tag.get_property("MODE", config.mode);
742 config.direction = input.tag.get_property("DIRECTION", config.direction);
743 config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
748 void Overlay::update_gui()
752 thread->window->lock_window("Overlay::update_gui");
753 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
754 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
755 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
756 thread->window->unlock_window();