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"
32 #include "vpatchgui.h"
48 static const char* mode_to_text(int mode);
51 static const char* direction_to_text(int direction);
59 static const char* output_to_text(int output_layer);
68 class OverlayMode : public BC_PopupMenu
71 OverlayMode(Overlay *plugin,
74 void create_objects();
79 class OverlayDirection : public BC_PopupMenu
82 OverlayDirection(Overlay *plugin,
85 void create_objects();
90 class OverlayOutput : public BC_PopupMenu
93 OverlayOutput(Overlay *plugin,
96 void create_objects();
102 class OverlayWindow : public PluginClientWindow
105 OverlayWindow(Overlay *plugin);
108 void create_objects();
113 OverlayDirection *direction;
114 OverlayOutput *output;
117 class Overlay : public PluginVClient
120 Overlay(PluginServer *server);
124 PLUGIN_CLASS_MEMBERS(OverlayConfig);
126 int process_buffer(VFrame **frame,
127 int64_t start_position,
130 int is_multichannel();
132 void save_data(KeyFrame *keyframe);
133 void read_data(KeyFrame *keyframe);
137 OverlayFrame *overlayer;
144 OverlayConfig::OverlayConfig()
146 mode = TRANSFER_NORMAL;
147 direction = OverlayConfig::BOTTOM_FIRST;
148 output_layer = OverlayConfig::TOP;
151 const char* OverlayConfig::mode_to_text(int mode)
153 return VModePatch::mode_to_text(mode);
156 const char* OverlayConfig::direction_to_text(int direction)
160 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
161 case OverlayConfig::TOP_FIRST: return _("Top first");
166 const char* OverlayConfig::output_to_text(int output_layer)
170 case OverlayConfig::TOP: return _("Top");
171 case OverlayConfig::BOTTOM: return _("Bottom");
184 OverlayWindow::OverlayWindow(Overlay *plugin)
185 : PluginClientWindow(plugin,
192 this->plugin = plugin;
195 OverlayWindow::~OverlayWindow()
199 void OverlayWindow::create_objects()
204 add_subwindow(title = new BC_Title(x, y, _("Mode:")));
205 add_subwindow(mode = new OverlayMode(plugin,
206 x + title->get_w() + 5,
208 mode->create_objects();
211 add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
212 add_subwindow(direction = new OverlayDirection(plugin,
213 x + title->get_w() + 5,
215 direction->create_objects();
218 add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
219 add_subwindow(output = new OverlayOutput(plugin,
220 x + title->get_w() + 5,
222 output->create_objects();
234 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
235 : BC_PopupMenu(x, y, 150,
236 OverlayConfig::mode_to_text(plugin->config.mode), 1)
238 this->plugin = plugin;
241 void OverlayMode::create_objects()
243 for(int i = 0; i < TRANSFER_TYPES; i++)
244 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
247 int OverlayMode::handle_event()
249 char *text = get_text();
251 for(int i = 0; i < TRANSFER_TYPES; i++)
253 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
255 plugin->config.mode = i;
260 plugin->send_configure_change();
265 OverlayDirection::OverlayDirection(Overlay *plugin,
271 OverlayConfig::direction_to_text(plugin->config.direction),
274 this->plugin = plugin;
277 void OverlayDirection::create_objects()
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)));
287 int OverlayDirection::handle_event()
289 char *text = get_text();
292 OverlayConfig::direction_to_text(
293 OverlayConfig::TOP_FIRST)))
294 plugin->config.direction = OverlayConfig::TOP_FIRST;
297 OverlayConfig::direction_to_text(
298 OverlayConfig::BOTTOM_FIRST)))
299 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
301 plugin->send_configure_change();
306 OverlayOutput::OverlayOutput(Overlay *plugin,
312 OverlayConfig::output_to_text(plugin->config.output_layer),
315 this->plugin = plugin;
318 void OverlayOutput::create_objects()
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)));
328 int OverlayOutput::handle_event()
330 char *text = get_text();
333 OverlayConfig::output_to_text(
334 OverlayConfig::TOP)))
335 plugin->config.output_layer = OverlayConfig::TOP;
338 OverlayConfig::output_to_text(
339 OverlayConfig::BOTTOM)))
340 plugin->config.output_layer = OverlayConfig::BOTTOM;
342 plugin->send_configure_change();
365 REGISTER_PLUGIN(Overlay)
372 Overlay::Overlay(PluginServer *server)
373 : PluginVClient(server)
384 if(overlayer) delete overlayer;
385 if(temp) delete temp;
390 int Overlay::process_buffer(VFrame **frame,
391 int64_t start_position,
394 load_configuration();
396 EDLSession* session = get_edlsession();
397 int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
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];
405 current_layer = input_layer;
406 read_frame(output, current_layer, // Direct copy the first layer
407 start_position, frame_rate, get_use_opengl());
409 if( --layers > 0 ) { // need 2 layers to do overlay
411 temp = new VFrame(frame[0]->get_w(), frame[0]->get_h(),
412 frame[0]->get_color_model(), 0);
414 while( --layers >= 0 ) {
415 current_layer += step;
416 read_frame(temp, current_layer,
417 start_position, frame_rate, get_use_opengl());
419 if(get_use_opengl()) {
425 overlayer = new OverlayFrame(get_project_smp() + 1);
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);
437 int Overlay::handle_opengl()
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"
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";
452 static const char *put_pixels_frag =
453 " result.rgb += chroma_offset;\n"
454 " gl_FragColor = result;\n"
460 #define GL_STD_FRAG(FN) static const char *blend_##FN##_frag = \
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" \
465 #define GL_VEC_FRAG(FN) static const char *blend_##FN##_frag = \
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" \
487 static const char *blend_NORMAL_frag =
488 " vec4 result = mix(src_color, src_color, src_color.a);\n";
490 static const char *blend_ADDITION_frag =
491 " vec4 result = dst_color + src_color;\n"
492 " result = clamp(result, 0.0, 1.0);\n";
494 static const char *blend_SUBTRACT_frag =
495 " vec4 result = dst_color - src_color;\n"
496 " result = clamp(result, 0.0, 1.0);\n";
498 static const char *blend_REPLACE_frag =
499 " vec4 result = src_color;\n";
501 GL_STD_FRAG(MULTIPLY);
506 GL_VEC_FRAG(LIGHTEN);
508 GL_STD_FRAG(DST_ATOP);
510 GL_STD_FRAG(DST_OUT);
511 GL_STD_FRAG(DST_OVER);
513 GL_STD_FRAG(SRC_ATOP);
515 GL_STD_FRAG(SRC_OUT);
516 GL_STD_FRAG(SRC_OVER);
520 GL_VEC_FRAG(OVERLAY);
524 GL_VEC_FRAG(HARDLIGHT);
525 GL_VEC_FRAG(SOFTLIGHT);
526 GL_VEC_FRAG(DIFFERENCE);
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
562 VFrame *dst = get_output(output_layer);
565 switch( config.mode ) {
566 case TRANSFER_REPLACE:
570 dst->enable_opengl();
574 case TRANSFER_NORMAL:
575 // Move destination to screen
576 if( dst->get_opengl_state() != VFrame::SCREEN ) {
578 dst->enable_opengl();
583 dst->enable_opengl();
586 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
592 dst->enable_opengl();
594 src->bind_texture(0);
595 dst->bind_texture(1);
597 const char *shader_stack[16];
598 memset(shader_stack,0, sizeof(shader_stack));
599 int current_shader = 0;
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);
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);
619 glActiveTexture(GL_TEXTURE1);
620 glDisable(GL_TEXTURE_2D);
624 glActiveTexture(GL_TEXTURE0);
625 glDisable(GL_TEXTURE_2D);
628 // get the data before something else uses the screen
629 dst->screen_to_ram();
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; }
642 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
646 int Overlay::load_configuration()
648 KeyFrame *prev_keyframe;
649 prev_keyframe = get_prev_keyframe(get_source_position());
650 read_data(prev_keyframe);
655 void Overlay::save_data(KeyFrame *keyframe)
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);
666 output.tag.set_title("/OVERLAY");
668 output.terminate_string();
671 void Overlay::read_data(KeyFrame *keyframe)
675 input.set_shared_input(keyframe->xbuf);
677 while(!input.read_tag())
679 if(input.tag.title_is("OVERLAY"))
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);
688 void Overlay::update_gui()
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();