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,
193 this->plugin = plugin;
196 OverlayWindow::~OverlayWindow()
200 void OverlayWindow::create_objects()
202 int xs5 = xS(5), xs10 = xS(10);
203 int ys10 = yS(10), ys30 = yS(30);
204 int x = xs10, y = ys10;
207 add_subwindow(title = new BC_Title(x, y, _("Mode:")));
208 add_subwindow(mode = new OverlayMode(plugin,
209 x + title->get_w() + xs5,
211 mode->create_objects();
214 add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
215 add_subwindow(direction = new OverlayDirection(plugin,
216 x + title->get_w() + xs5,
218 direction->create_objects();
221 add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
222 add_subwindow(output = new OverlayOutput(plugin,
223 x + title->get_w() + xs5,
225 output->create_objects();
237 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
238 : BC_PopupMenu(x, y, xS(150),
239 OverlayConfig::mode_to_text(plugin->config.mode), 1)
241 this->plugin = plugin;
244 void OverlayMode::create_objects()
246 for(int i = 0; i < TRANSFER_TYPES; i++)
247 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
250 int OverlayMode::handle_event()
252 char *text = get_text();
254 for(int i = 0; i < TRANSFER_TYPES; i++)
256 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
258 plugin->config.mode = i;
263 plugin->send_configure_change();
268 OverlayDirection::OverlayDirection(Overlay *plugin,
271 : BC_PopupMenu(x, y, xS(150),
272 OverlayConfig::direction_to_text(plugin->config.direction),
275 this->plugin = plugin;
278 void OverlayDirection::create_objects()
280 add_item(new BC_MenuItem(
281 OverlayConfig::direction_to_text(
282 OverlayConfig::TOP_FIRST)));
283 add_item(new BC_MenuItem(
284 OverlayConfig::direction_to_text(
285 OverlayConfig::BOTTOM_FIRST)));
288 int OverlayDirection::handle_event()
290 char *text = get_text();
293 OverlayConfig::direction_to_text(
294 OverlayConfig::TOP_FIRST)))
295 plugin->config.direction = OverlayConfig::TOP_FIRST;
298 OverlayConfig::direction_to_text(
299 OverlayConfig::BOTTOM_FIRST)))
300 plugin->config.direction = OverlayConfig::BOTTOM_FIRST;
302 plugin->send_configure_change();
307 OverlayOutput::OverlayOutput(Overlay *plugin,
310 : BC_PopupMenu(x, y, xS(100),
311 OverlayConfig::output_to_text(plugin->config.output_layer),
314 this->plugin = plugin;
317 void OverlayOutput::create_objects()
319 add_item(new BC_MenuItem(
320 OverlayConfig::output_to_text(
321 OverlayConfig::TOP)));
322 add_item(new BC_MenuItem(
323 OverlayConfig::output_to_text(
324 OverlayConfig::BOTTOM)));
327 int OverlayOutput::handle_event()
329 char *text = get_text();
332 OverlayConfig::output_to_text(
333 OverlayConfig::TOP)))
334 plugin->config.output_layer = OverlayConfig::TOP;
337 OverlayConfig::output_to_text(
338 OverlayConfig::BOTTOM)))
339 plugin->config.output_layer = OverlayConfig::BOTTOM;
341 plugin->send_configure_change();
364 REGISTER_PLUGIN(Overlay)
371 Overlay::Overlay(PluginServer *server)
372 : PluginVClient(server)
383 if(overlayer) delete overlayer;
384 if(temp) delete temp;
389 int Overlay::process_buffer(VFrame **frame,
390 int64_t start_position,
393 load_configuration();
395 EDLSession* session = get_edl()->session;
396 int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
398 int step = config.direction == OverlayConfig::BOTTOM_FIRST ? -1 : 1;
399 int layers = get_total_buffers();
400 input_layer = config.direction == OverlayConfig::BOTTOM_FIRST ? layers-1 : 0;
401 output_layer = config.output_layer == OverlayConfig::TOP ? 0 : layers-1;
402 VFrame *output = frame[output_layer];
404 current_layer = input_layer;
405 read_frame(output, current_layer, // Direct copy the first layer
406 start_position, frame_rate, get_use_opengl());
408 if( --layers > 0 ) { // need 2 layers to do overlay
410 temp = new VFrame(frame[0]->get_w(), frame[0]->get_h(),
411 frame[0]->get_color_model(), 0);
413 while( --layers >= 0 ) {
414 current_layer += step;
415 read_frame(temp, current_layer,
416 start_position, frame_rate, get_use_opengl());
418 if(get_use_opengl()) {
424 overlayer = new OverlayFrame(get_project_smp() + 1);
426 overlayer->overlay(output, temp,
427 0, 0, output->get_w(), output->get_h(),
428 0, 0, output->get_w(), output->get_h(),
429 1, config.mode, interpolation_type);
436 int Overlay::handle_opengl()
439 static const char *get_pixels_frag =
440 "uniform sampler2D src_tex;\n"
441 "uniform sampler2D dst_tex;\n"
442 "uniform vec2 dst_tex_dimensions;\n"
443 "uniform vec3 chroma_offset;\n"
446 " vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
447 " vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
448 " src_color.rgb -= chroma_offset;\n"
449 " dst_color.rgb -= chroma_offset;\n";
451 static const char *put_pixels_frag =
452 " result.rgb += chroma_offset;\n"
453 " gl_FragColor = result;\n"
459 #define GL_STD_FRAG(FN) static const char *blend_##FN##_frag = \
461 " result.rgb = " SS(COLOR_##FN(1.0, src_color.rgb, src_color.a, dst_color.rgb, dst_color.a)) ";\n" \
462 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a))";\n" \
464 #define GL_VEC_FRAG(FN) static const char *blend_##FN##_frag = \
466 " result.r = " SS(COLOR_##FN(1.0, src_color.r, src_color.a, dst_color.r, dst_color.a)) ";\n" \
467 " result.g = " SS(COLOR_##FN(1.0, src_color.g, src_color.a, dst_color.g, dst_color.a)) ";\n" \
468 " result.b = " SS(COLOR_##FN(1.0, src_color.b, src_color.a, dst_color.b, dst_color.a)) ";\n" \
469 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a)) ";\n" \
470 " result = clamp(result, 0.0, 1.0);\n" \
486 static const char *blend_REPLACE_frag =
487 " vec4 result = src_color;\n";
490 GL_VEC_FRAG(ADDITION);
491 GL_VEC_FRAG(SUBTRACT);
492 GL_STD_FRAG(MULTIPLY);
497 GL_VEC_FRAG(LIGHTEN);
499 GL_STD_FRAG(DST_ATOP);
501 GL_STD_FRAG(DST_OUT);
502 GL_STD_FRAG(DST_OVER);
504 GL_STD_FRAG(SRC_ATOP);
506 GL_STD_FRAG(SRC_OUT);
507 GL_STD_FRAG(SRC_OVER);
511 GL_VEC_FRAG(OVERLAY);
515 GL_VEC_FRAG(HARDLIGHT);
516 GL_VEC_FRAG(SOFTLIGHT);
517 GL_VEC_FRAG(DIFFERENCE);
519 static const char * const overlay_shaders[TRANSFER_TYPES] = {
520 blend_NORMAL_frag, // TRANSFER_NORMAL
521 blend_ADDITION_frag, // TRANSFER_ADDITION
522 blend_SUBTRACT_frag, // TRANSFER_SUBTRACT
523 blend_MULTIPLY_frag, // TRANSFER_MULTIPLY
524 blend_DIVIDE_frag, // TRANSFER_DIVIDE
525 blend_REPLACE_frag, // TRANSFER_REPLACE
526 blend_MAX_frag, // TRANSFER_MAX
527 blend_MIN_frag, // TRANSFER_MIN
528 blend_DARKEN_frag, // TRANSFER_DARKEN
529 blend_LIGHTEN_frag, // TRANSFER_LIGHTEN
530 blend_DST_frag, // TRANSFER_DST
531 blend_DST_ATOP_frag, // TRANSFER_DST_ATOP
532 blend_DST_IN_frag, // TRANSFER_DST_IN
533 blend_DST_OUT_frag, // TRANSFER_DST_OUT
534 blend_DST_OVER_frag, // TRANSFER_DST_OVER
535 blend_SRC_frag, // TRANSFER_SRC
536 blend_SRC_ATOP_frag, // TRANSFER_SRC_ATOP
537 blend_SRC_IN_frag, // TRANSFER_SRC_IN
538 blend_SRC_OUT_frag, // TRANSFER_SRC_OUT
539 blend_SRC_OVER_frag, // TRANSFER_SRC_OVER
540 blend_AND_frag, // TRANSFER_AND
541 blend_OR_frag, // TRANSFER_OR
542 blend_XOR_frag, // TRANSFER_XOR
543 blend_OVERLAY_frag, // TRANSFER_OVERLAY
544 blend_SCREEN_frag, // TRANSFER_SCREEN
545 blend_BURN_frag, // TRANSFER_BURN
546 blend_DODGE_frag, // TRANSFER_DODGE
547 blend_HARDLIGHT_frag, // TRANSFER_HARDLIGHT
548 blend_SOFTLIGHT_frag, // TRANSFER_SOFTLIGHT
549 blend_DIFFERENCE_frag, // TRANSFER_DIFFERENCE
553 VFrame *dst = get_output(output_layer);
556 switch( config.mode ) {
557 case TRANSFER_REPLACE:
561 dst->enable_opengl();
568 dst->enable_opengl();
570 src->bind_texture(0);
571 dst->bind_texture(1);
573 const char *shader_stack[16];
574 memset(shader_stack,0, sizeof(shader_stack));
575 int current_shader = 0;
577 shader_stack[current_shader++] = get_pixels_frag;
578 shader_stack[current_shader++] = overlay_shaders[config.mode];
579 shader_stack[current_shader++] = put_pixels_frag;
580 shader_stack[current_shader] = 0;
581 unsigned int shader = VFrame::make_shader(shader_stack);
583 glUseProgram(shader);
584 glUniform1i(glGetUniformLocation(shader, "src_tex"), 0);
585 glUniform1i(glGetUniformLocation(shader, "dst_tex"), 1);
586 glUniform2f(glGetUniformLocation(shader, "dst_tex_dimensions"),
587 (float)dst->get_texture_w(), (float)dst->get_texture_h());
588 float chroma_offset = BC_CModels::is_yuv(dst->get_color_model()) ? 0.5 : 0.0;
589 glUniform3f(glGetUniformLocation(shader, "chroma_offset"),
590 0.0, chroma_offset, chroma_offset);
595 glActiveTexture(GL_TEXTURE1);
596 glDisable(GL_TEXTURE_2D);
600 glActiveTexture(GL_TEXTURE0);
601 glDisable(GL_TEXTURE_2D);
604 // get the data before something else uses the screen
605 dst->screen_to_ram();
611 const char* Overlay::plugin_title() { return N_("Overlay"); }
612 int Overlay::is_realtime() { return 1; }
613 int Overlay::is_multichannel() { return 1; }
614 int Overlay::is_synthesis() { return 1; }
618 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
622 int Overlay::load_configuration()
624 KeyFrame *prev_keyframe;
625 prev_keyframe = get_prev_keyframe(get_source_position());
626 read_data(prev_keyframe);
631 void Overlay::save_data(KeyFrame *keyframe)
635 // cause data to be stored directly in text
636 output.set_shared_output(keyframe->xbuf);
637 output.tag.set_title("OVERLAY");
638 output.tag.set_property("MODE", config.mode);
639 output.tag.set_property("DIRECTION", config.direction);
640 output.tag.set_property("OUTPUT_LAYER", config.output_layer);
642 output.tag.set_title("/OVERLAY");
644 output.terminate_string();
647 void Overlay::read_data(KeyFrame *keyframe)
651 input.set_shared_input(keyframe->xbuf);
653 while(!input.read_tag())
655 if(input.tag.title_is("OVERLAY"))
657 config.mode = input.tag.get_property("MODE", config.mode);
658 config.direction = input.tag.get_property("DIRECTION", config.direction);
659 config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
664 void Overlay::update_gui()
668 thread->window->lock_window("Overlay::update_gui");
669 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
670 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
671 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
672 thread->window->unlock_window();