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()
205 add_subwindow(title = new BC_Title(x, y, _("Mode:")));
206 add_subwindow(mode = new OverlayMode(plugin,
207 x + title->get_w() + 5,
209 mode->create_objects();
212 add_subwindow(title = new BC_Title(x, y, _("Layer order:")));
213 add_subwindow(direction = new OverlayDirection(plugin,
214 x + title->get_w() + 5,
216 direction->create_objects();
219 add_subwindow(title = new BC_Title(x, y, _("Output layer:")));
220 add_subwindow(output = new OverlayOutput(plugin,
221 x + title->get_w() + 5,
223 output->create_objects();
235 OverlayMode::OverlayMode(Overlay *plugin, int x, int y)
236 : BC_PopupMenu(x, y, 150,
237 OverlayConfig::mode_to_text(plugin->config.mode), 1)
239 this->plugin = plugin;
242 void OverlayMode::create_objects()
244 for(int i = 0; i < TRANSFER_TYPES; i++)
245 add_item(new BC_MenuItem(OverlayConfig::mode_to_text(i)));
248 int OverlayMode::handle_event()
250 char *text = get_text();
252 for(int i = 0; i < TRANSFER_TYPES; i++)
254 if(!strcmp(text, OverlayConfig::mode_to_text(i)))
256 plugin->config.mode = i;
261 plugin->send_configure_change();
266 OverlayDirection::OverlayDirection(Overlay *plugin,
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,
313 OverlayConfig::output_to_text(plugin->config.output_layer),
316 this->plugin = plugin;
319 void OverlayOutput::create_objects()
321 add_item(new BC_MenuItem(
322 OverlayConfig::output_to_text(
323 OverlayConfig::TOP)));
324 add_item(new BC_MenuItem(
325 OverlayConfig::output_to_text(
326 OverlayConfig::BOTTOM)));
329 int OverlayOutput::handle_event()
331 char *text = get_text();
334 OverlayConfig::output_to_text(
335 OverlayConfig::TOP)))
336 plugin->config.output_layer = OverlayConfig::TOP;
339 OverlayConfig::output_to_text(
340 OverlayConfig::BOTTOM)))
341 plugin->config.output_layer = OverlayConfig::BOTTOM;
343 plugin->send_configure_change();
366 REGISTER_PLUGIN(Overlay)
373 Overlay::Overlay(PluginServer *server)
374 : PluginVClient(server)
385 if(overlayer) delete overlayer;
386 if(temp) delete temp;
391 int Overlay::process_buffer(VFrame **frame,
392 int64_t start_position,
395 load_configuration();
397 EDLSession* session = get_edl()->session;
398 int interpolation_type = session ? session->interpolation_type : NEAREST_NEIGHBOR;
400 int step = config.direction == OverlayConfig::BOTTOM_FIRST ? -1 : 1;
401 int layers = get_total_buffers();
402 input_layer = config.direction == OverlayConfig::BOTTOM_FIRST ? layers-1 : 0;
403 output_layer = config.output_layer == OverlayConfig::TOP ? 0 : layers-1;
404 VFrame *output = frame[output_layer];
406 current_layer = input_layer;
407 read_frame(output, current_layer, // Direct copy the first layer
408 start_position, frame_rate, get_use_opengl());
410 if( --layers > 0 ) { // need 2 layers to do overlay
412 temp = new VFrame(frame[0]->get_w(), frame[0]->get_h(),
413 frame[0]->get_color_model(), 0);
415 while( --layers >= 0 ) {
416 current_layer += step;
417 read_frame(temp, current_layer,
418 start_position, frame_rate, get_use_opengl());
420 if(get_use_opengl()) {
426 overlayer = new OverlayFrame(get_project_smp() + 1);
428 overlayer->overlay(output, temp,
429 0, 0, output->get_w(), output->get_h(),
430 0, 0, output->get_w(), output->get_h(),
431 1, config.mode, interpolation_type);
438 int Overlay::handle_opengl()
441 static const char *get_pixels_frag =
442 "uniform sampler2D src_tex;\n"
443 "uniform sampler2D dst_tex;\n"
444 "uniform vec2 dst_tex_dimensions;\n"
445 "uniform vec3 chroma_offset;\n"
448 " vec4 dst_color = texture2D(dst_tex, gl_FragCoord.xy / dst_tex_dimensions);\n"
449 " vec4 src_color = texture2D(src_tex, gl_TexCoord[0].st);\n"
450 " src_color.rgb -= chroma_offset;\n"
451 " dst_color.rgb -= chroma_offset;\n";
453 static const char *put_pixels_frag =
454 " result.rgb += chroma_offset;\n"
455 " gl_FragColor = result;\n"
461 #define GL_STD_FRAG(FN) static const char *blend_##FN##_frag = \
463 " result.rgb = " SS(COLOR_##FN(1.0, src_color.rgb, src_color.a, dst_color.rgb, dst_color.a)) ";\n" \
464 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a))";\n" \
466 #define GL_VEC_FRAG(FN) static const char *blend_##FN##_frag = \
468 " result.r = " SS(COLOR_##FN(1.0, src_color.r, src_color.a, dst_color.r, dst_color.a)) ";\n" \
469 " result.g = " SS(COLOR_##FN(1.0, src_color.g, src_color.a, dst_color.g, dst_color.a)) ";\n" \
470 " result.b = " SS(COLOR_##FN(1.0, src_color.b, src_color.a, dst_color.b, dst_color.a)) ";\n" \
471 " result.a = " SS(ALPHA_##FN(1.0, src_color.a, dst_color.a)) ";\n" \
472 " result = clamp(result, 0.0, 1.0);\n" \
488 static const char *blend_REPLACE_frag =
489 " vec4 result = src_color;\n";
492 GL_VEC_FRAG(ADDITION);
493 GL_VEC_FRAG(SUBTRACT);
494 GL_STD_FRAG(MULTIPLY);
499 GL_VEC_FRAG(LIGHTEN);
501 GL_STD_FRAG(DST_ATOP);
503 GL_STD_FRAG(DST_OUT);
504 GL_STD_FRAG(DST_OVER);
506 GL_STD_FRAG(SRC_ATOP);
508 GL_STD_FRAG(SRC_OUT);
509 GL_STD_FRAG(SRC_OVER);
513 GL_VEC_FRAG(OVERLAY);
517 GL_VEC_FRAG(HARDLIGHT);
518 GL_VEC_FRAG(SOFTLIGHT);
519 GL_VEC_FRAG(DIFFERENCE);
521 static const char * const overlay_shaders[TRANSFER_TYPES] = {
522 blend_NORMAL_frag, // TRANSFER_NORMAL
523 blend_ADDITION_frag, // TRANSFER_ADDITION
524 blend_SUBTRACT_frag, // TRANSFER_SUBTRACT
525 blend_MULTIPLY_frag, // TRANSFER_MULTIPLY
526 blend_DIVIDE_frag, // TRANSFER_DIVIDE
527 blend_REPLACE_frag, // TRANSFER_REPLACE
528 blend_MAX_frag, // TRANSFER_MAX
529 blend_MIN_frag, // TRANSFER_MIN
530 blend_DARKEN_frag, // TRANSFER_DARKEN
531 blend_LIGHTEN_frag, // TRANSFER_LIGHTEN
532 blend_DST_frag, // TRANSFER_DST
533 blend_DST_ATOP_frag, // TRANSFER_DST_ATOP
534 blend_DST_IN_frag, // TRANSFER_DST_IN
535 blend_DST_OUT_frag, // TRANSFER_DST_OUT
536 blend_DST_OVER_frag, // TRANSFER_DST_OVER
537 blend_SRC_frag, // TRANSFER_SRC
538 blend_SRC_ATOP_frag, // TRANSFER_SRC_ATOP
539 blend_SRC_IN_frag, // TRANSFER_SRC_IN
540 blend_SRC_OUT_frag, // TRANSFER_SRC_OUT
541 blend_SRC_OVER_frag, // TRANSFER_SRC_OVER
542 blend_AND_frag, // TRANSFER_AND
543 blend_OR_frag, // TRANSFER_OR
544 blend_XOR_frag, // TRANSFER_XOR
545 blend_OVERLAY_frag, // TRANSFER_OVERLAY
546 blend_SCREEN_frag, // TRANSFER_SCREEN
547 blend_BURN_frag, // TRANSFER_BURN
548 blend_DODGE_frag, // TRANSFER_DODGE
549 blend_HARDLIGHT_frag, // TRANSFER_HARDLIGHT
550 blend_SOFTLIGHT_frag, // TRANSFER_SOFTLIGHT
551 blend_DIFFERENCE_frag, // TRANSFER_DIFFERENCE
555 VFrame *dst = get_output(output_layer);
558 switch( config.mode ) {
559 case TRANSFER_REPLACE:
563 dst->enable_opengl();
570 dst->enable_opengl();
572 src->bind_texture(0);
573 dst->bind_texture(1);
575 const char *shader_stack[16];
576 memset(shader_stack,0, sizeof(shader_stack));
577 int current_shader = 0;
579 shader_stack[current_shader++] = get_pixels_frag;
580 shader_stack[current_shader++] = overlay_shaders[config.mode];
581 shader_stack[current_shader++] = put_pixels_frag;
582 shader_stack[current_shader] = 0;
583 unsigned int shader = VFrame::make_shader(shader_stack);
585 glUseProgram(shader);
586 glUniform1i(glGetUniformLocation(shader, "src_tex"), 0);
587 glUniform1i(glGetUniformLocation(shader, "dst_tex"), 1);
588 glUniform2f(glGetUniformLocation(shader, "dst_tex_dimensions"),
589 (float)dst->get_texture_w(), (float)dst->get_texture_h());
590 float chroma_offset = BC_CModels::is_yuv(dst->get_color_model()) ? 0.5 : 0.0;
591 glUniform3f(glGetUniformLocation(shader, "chroma_offset"),
592 0.0, chroma_offset, chroma_offset);
597 glActiveTexture(GL_TEXTURE1);
598 glDisable(GL_TEXTURE_2D);
602 glActiveTexture(GL_TEXTURE0);
603 glDisable(GL_TEXTURE_2D);
606 // get the data before something else uses the screen
607 dst->screen_to_ram();
613 const char* Overlay::plugin_title() { return N_("Overlay"); }
614 int Overlay::is_realtime() { return 1; }
615 int Overlay::is_multichannel() { return 1; }
616 int Overlay::is_synthesis() { return 1; }
620 NEW_WINDOW_MACRO(Overlay, OverlayWindow)
624 int Overlay::load_configuration()
626 KeyFrame *prev_keyframe;
627 prev_keyframe = get_prev_keyframe(get_source_position());
628 read_data(prev_keyframe);
633 void Overlay::save_data(KeyFrame *keyframe)
637 // cause data to be stored directly in text
638 output.set_shared_output(keyframe->xbuf);
639 output.tag.set_title("OVERLAY");
640 output.tag.set_property("MODE", config.mode);
641 output.tag.set_property("DIRECTION", config.direction);
642 output.tag.set_property("OUTPUT_LAYER", config.output_layer);
644 output.tag.set_title("/OVERLAY");
646 output.terminate_string();
649 void Overlay::read_data(KeyFrame *keyframe)
653 input.set_shared_input(keyframe->xbuf);
655 while(!input.read_tag())
657 if(input.tag.title_is("OVERLAY"))
659 config.mode = input.tag.get_property("MODE", config.mode);
660 config.direction = input.tag.get_property("DIRECTION", config.direction);
661 config.output_layer = input.tag.get_property("OUTPUT_LAYER", config.output_layer);
666 void Overlay::update_gui()
670 thread->window->lock_window("Overlay::update_gui");
671 ((OverlayWindow*)thread->window)->mode->set_text(OverlayConfig::mode_to_text(config.mode));
672 ((OverlayWindow*)thread->window)->direction->set_text(OverlayConfig::direction_to_text(config.direction));
673 ((OverlayWindow*)thread->window)->output->set_text(OverlayConfig::output_to_text(config.output_layer));
674 thread->window->unlock_window();