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"
26 #include "edlsession.h"
31 #include "overlayframe.h"
32 #include "pluginvclient.h"
33 #include "vpatchgui.h"
49 static const char* mode_to_text(int mode);
52 static const char* direction_to_text(int direction);
60 static const char* output_to_text(int output_layer);
69 class OverlayMode : public BC_PopupMenu
72 OverlayMode(Overlay *plugin,
75 void create_objects();
80 class OverlayDirection : public BC_PopupMenu
83 OverlayDirection(Overlay *plugin,
86 void create_objects();
91 class OverlayOutput : public BC_PopupMenu
94 OverlayOutput(Overlay *plugin,
97 void create_objects();
103 class OverlayWindow : public PluginClientWindow
106 OverlayWindow(Overlay *plugin);
109 void create_objects();
114 OverlayDirection *direction;
115 OverlayOutput *output;
118 class Overlay : public PluginVClient
121 Overlay(PluginServer *server);
125 PLUGIN_CLASS_MEMBERS(OverlayConfig);
127 int process_buffer(VFrame **frame,
128 int64_t start_position,
131 int is_multichannel();
133 void save_data(KeyFrame *keyframe);
134 void read_data(KeyFrame *keyframe);
138 OverlayFrame *overlayer;
145 OverlayConfig::OverlayConfig()
147 mode = TRANSFER_NORMAL;
148 direction = OverlayConfig::BOTTOM_FIRST;
149 output_layer = OverlayConfig::TOP;
152 const char* OverlayConfig::mode_to_text(int mode)
154 return VModePatch::mode_to_text(mode);
157 const char* OverlayConfig::direction_to_text(int direction)
161 case OverlayConfig::BOTTOM_FIRST: return _("Bottom first");
162 case OverlayConfig::TOP_FIRST: return _("Top first");
167 const char* OverlayConfig::output_to_text(int output_layer)
171 case OverlayConfig::TOP: return _("Top");
172 case OverlayConfig::BOTTOM: return _("Bottom");
185 OverlayWindow::OverlayWindow(Overlay *plugin)
186 : PluginClientWindow(plugin, xS(300), yS(100), xS(300), yS(100), 0)
188 this->plugin = plugin;
191 OverlayWindow::~OverlayWindow()
195 void OverlayWindow::create_objects()
197 int xs5 = xS(5), xs10 = xS(10);
198 int ys10 = yS(10), ys30 = yS(30);
199 int x = xs10, y = ys10;
202 add_subwindow(title = new BC_Title(x, y, _("Mode:")));
203 add_subwindow(mode = new OverlayMode(plugin,
204 x + title->get_w() + xs5,
206 mode->create_objects();
209 add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
210 add_subwindow(direction = new OverlayDirection(plugin,
211 x + title->get_w() + xs5,
213 direction->create_objects();
216 add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
217 add_subwindow(output = new OverlayOutput(plugin,
218 x + title->get_w() + xs5,
220 output->create_objects();
232 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
233 : BC_PopupMenu(x, y, xS(150),
234 OverlayConfig::mode_to_text(plugin->config.mode), 1)
236 this->plugin = plugin;
239 void OverlayMode::create_objects()
241 for(int i = 0; i < TRANSFER_TYPES; i++)
242 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
245 int OverlayMode::handle_event()
247 char *text = get_text();
249 for(int i = 0; i < TRANSFER_TYPES; i++)
251 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
253 plugin->config.mode = i;
258 plugin->send_configure_change();
263 OverlayDirection::OverlayDirection(Overlay *plugin,
266 : BC_PopupMenu(x, y, xS(150),
267 OverlayConfig::direction_to_text(plugin->config.direction),
270 this->plugin = plugin;
273 void OverlayDirection::create_objects()
275 add_item(new BC_MenuItem(
276 OverlayConfig::direction_to_text(
277 OverlayConfig::TOP_FIRST)));
278 add_item(new BC_MenuItem(
279 OverlayConfig::direction_to_text(
280 OverlayConfig::BOTTOM_FIRST)));
283 int OverlayDirection::handle_event()
285 char *text = get_text();
288 OverlayConfig::direction_to_text(
289 OverlayConfig::TOP_FIRST)))
290 plugin->config.direction = OverlayConfig::TOP_FIRST;
293 OverlayConfig::direction_to_text(
294 OverlayConfig::BOTTOM_FIRST)))
295 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
297 plugin->send_configure_change();
302 OverlayOutput::OverlayOutput(Overlay *plugin,
305 : BC_PopupMenu(x, y, xS(100),
306 OverlayConfig::output_to_text(plugin->config.output_layer),
309 this->plugin = plugin;
312 void OverlayOutput::create_objects()
314 add_item(new BC_MenuItem(
315 OverlayConfig::output_to_text(
316 OverlayConfig::TOP)));
317 add_item(new BC_MenuItem(
318 OverlayConfig::output_to_text(
319 OverlayConfig::BOTTOM)));
322 int OverlayOutput::handle_event()
324 char *text = get_text();
327 OverlayConfig::output_to_text(
328 OverlayConfig::TOP)))
329 plugin->config.output_layer = OverlayConfig::TOP;
332 OverlayConfig::output_to_text(
333 OverlayConfig::BOTTOM)))
334 plugin->config.output_layer = OverlayConfig::BOTTOM;
336 plugin->send_configure_change();
359 REGISTER_PLUGIN(Overlay)
366 Overlay::Overlay(PluginServer *server)
367 : PluginVClient(server)
378 if(overlayer) delete overlayer;
379 if(temp) delete temp;
384 int Overlay::process_buffer(VFrame **frame,
385 int64_t start_position,
388 load_configuration();
390 EDLSession* session = get_edl()->session;
391 int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
393 int step = config.direction == OverlayConfig::BOTTOM_FIRST ? -1 : 1;
394 int layers = get_total_buffers();
395 input_layer = config.direction == OverlayConfig::BOTTOM_FIRST ? layers-1 : 0;
396 output_layer = config.output_layer == OverlayConfig::TOP ? 0 : layers-1;
397 VFrame *output = frame[output_layer];
399 current_layer = input_layer;
400 read_frame(output, current_layer, // Direct copy the first layer
401 start_position, frame_rate, get_use_opengl());
403 if( --layers > 0 ) { // need 2 layers to do overlay
405 temp = new VFrame(frame[0]->get_w(), frame[0]->get_h(),
406 frame[0]->get_color_model(), 0);
408 while( --layers >= 0 ) {
409 current_layer += step;
410 read_frame(temp, current_layer,
411 start_position, frame_rate, get_use_opengl());
413 if(get_use_opengl()) {
419 overlayer = new OverlayFrame(get_project_smp() + 1);
421 overlayer->overlay(output, temp,
422 0, 0, output->get_w(), output->get_h(),
423 0, 0, output->get_w(), output->get_h(),
424 1, config.mode, interpolation_type);
431 int Overlay::handle_opengl()
434 static const char *get_pixels_frag =
435 "uniform sampler2D src_tex;\n"
436 "uniform sampler2D dst_tex;\n"
437 "uniform vec2 dst_tex_dimensions;\n"
438 "uniform vec3 chroma_offset;\n"
441 " vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
442 " vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
443 " src_color.rgb -= chroma_offset;\n"
444 " dst_color.rgb -= chroma_offset;\n";
446 static const char *put_pixels_frag =
447 " result.rgb += chroma_offset;\n"
448 " gl_FragColor = result;\n"
454 #define GL_STD_FRAG(FN) static const char *blend_##FN##_frag = \
456 " result.rgb = " SS(COLOR_##FN(1.0, src_color.rgb, src_color.a, dst_color.rgb, dst_color.a)) ";\n" \
457 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a))";\n" \
459 #define GL_VEC_FRAG(FN) static const char *blend_##FN##_frag = \
461 " result.r = " SS(COLOR_##FN(1.0, src_color.r, src_color.a, dst_color.r, dst_color.a)) ";\n" \
462 " result.g = " SS(COLOR_##FN(1.0, src_color.g, src_color.a, dst_color.g, dst_color.a)) ";\n" \
463 " result.b = " SS(COLOR_##FN(1.0, src_color.b, src_color.a, dst_color.b, dst_color.a)) ";\n" \
464 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a)) ";\n" \
465 " result = clamp(result, 0.0, 1.0);\n" \
481 static const char *blend_REPLACE_frag =
482 " vec4 result = src_color;\n";
485 GL_VEC_FRAG(ADDITION);
486 GL_VEC_FRAG(SUBTRACT);
487 GL_STD_FRAG(MULTIPLY);
492 GL_VEC_FRAG(LIGHTEN);
494 GL_STD_FRAG(DST_ATOP);
496 GL_STD_FRAG(DST_OUT);
497 GL_STD_FRAG(DST_OVER);
499 GL_STD_FRAG(SRC_ATOP);
501 GL_STD_FRAG(SRC_OUT);
502 GL_STD_FRAG(SRC_OVER);
506 GL_VEC_FRAG(OVERLAY);
510 GL_VEC_FRAG(HARDLIGHT);
511 GL_VEC_FRAG(SOFTLIGHT);
512 GL_VEC_FRAG(DIFFERENCE);
514 static const char * const overlay_shaders[TRANSFER_TYPES] = {
515 blend_NORMAL_frag, // TRANSFER_NORMAL
516 blend_ADDITION_frag, // TRANSFER_ADDITION
517 blend_SUBTRACT_frag, // TRANSFER_SUBTRACT
518 blend_MULTIPLY_frag, // TRANSFER_MULTIPLY
519 blend_DIVIDE_frag, // TRANSFER_DIVIDE
520 blend_REPLACE_frag, // TRANSFER_REPLACE
521 blend_MAX_frag, // TRANSFER_MAX
522 blend_MIN_frag, // TRANSFER_MIN
523 blend_DARKEN_frag, // TRANSFER_DARKEN
524 blend_LIGHTEN_frag, // TRANSFER_LIGHTEN
525 blend_DST_frag, // TRANSFER_DST
526 blend_DST_ATOP_frag, // TRANSFER_DST_ATOP
527 blend_DST_IN_frag, // TRANSFER_DST_IN
528 blend_DST_OUT_frag, // TRANSFER_DST_OUT
529 blend_DST_OVER_frag, // TRANSFER_DST_OVER
530 blend_SRC_frag, // TRANSFER_SRC
531 blend_SRC_ATOP_frag, // TRANSFER_SRC_ATOP
532 blend_SRC_IN_frag, // TRANSFER_SRC_IN
533 blend_SRC_OUT_frag, // TRANSFER_SRC_OUT
534 blend_SRC_OVER_frag, // TRANSFER_SRC_OVER
535 blend_AND_frag, // TRANSFER_AND
536 blend_OR_frag, // TRANSFER_OR
537 blend_XOR_frag, // TRANSFER_XOR
538 blend_OVERLAY_frag, // TRANSFER_OVERLAY
539 blend_SCREEN_frag, // TRANSFER_SCREEN
540 blend_BURN_frag, // TRANSFER_BURN
541 blend_DODGE_frag, // TRANSFER_DODGE
542 blend_HARDLIGHT_frag, // TRANSFER_HARDLIGHT
543 blend_SOFTLIGHT_frag, // TRANSFER_SOFTLIGHT
544 blend_DIFFERENCE_frag, // TRANSFER_DIFFERENCE
548 VFrame *dst = get_output(output_layer);
551 switch( config.mode ) {
552 case TRANSFER_REPLACE:
556 dst->enable_opengl();
563 dst->enable_opengl();
565 src->bind_texture(0);
566 dst->bind_texture(1);
568 const char *shader_stack[16];
569 memset(shader_stack,0, sizeof(shader_stack));
570 int current_shader = 0;
572 shader_stack[current_shader++] = get_pixels_frag;
573 shader_stack[current_shader++] = overlay_shaders[config.mode];
574 shader_stack[current_shader++] = put_pixels_frag;
575 shader_stack[current_shader] = 0;
576 unsigned int shader = VFrame::make_shader(shader_stack);
578 glUseProgram(shader);
579 glUniform1i(glGetUniformLocation(shader, "src_tex"), 0);
580 glUniform1i(glGetUniformLocation(shader, "dst_tex"), 1);
581 glUniform2f(glGetUniformLocation(shader, "dst_tex_dimensions"),
582 (float)dst->get_texture_w(), (float)dst->get_texture_h());
583 float chroma_offset = BC_CModels::is_yuv(dst->get_color_model()) ? 0.5 : 0.0;
584 glUniform3f(glGetUniformLocation(shader, "chroma_offset"),
585 0.0, chroma_offset, chroma_offset);
590 glActiveTexture(GL_TEXTURE1);
591 glDisable(GL_TEXTURE_2D);
595 glActiveTexture(GL_TEXTURE0);
596 glDisable(GL_TEXTURE_2D);
599 // get the data before something else uses the screen
600 dst->screen_to_ram();
606 const char* Overlay::plugin_title() { return N_("Overlay"); }
607 int Overlay::is_realtime() { return 1; }
608 int Overlay::is_multichannel() { return 1; }
609 int Overlay::is_synthesis() { return 1; }
613 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
617 int Overlay::load_configuration()
619 KeyFrame *prev_keyframe;
620 prev_keyframe = get_prev_keyframe(get_source_position());
621 read_data(prev_keyframe);
626 void Overlay::save_data(KeyFrame *keyframe)
630 // cause data to be stored directly in text
631 output.set_shared_output(keyframe->xbuf);
632 output.tag.set_title("OVERLAY");
633 output.tag.set_property("MODE", config.mode);
634 output.tag.set_property("DIRECTION", config.direction);
635 output.tag.set_property("OUTPUT_LAYER", config.output_layer);
637 output.tag.set_title("/OVERLAY");
639 output.terminate_string();
642 void Overlay::read_data(KeyFrame *keyframe)
646 input.set_shared_input(keyframe->xbuf);
648 while(!input.read_tag())
650 if(input.tag.title_is("OVERLAY"))
652 config.mode = input.tag.get_property("MODE", config.mode);
653 config.direction = input.tag.get_property("DIRECTION", config.direction);
654 config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
659 void Overlay::update_gui()
663 thread->window->lock_window("Overlay::update_gui");
664 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
665 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
666 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
667 thread->window->unlock_window();