/* * CINELERRA * Copyright (C) 2009 Adam Williams * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #define GL_GLEXT_PROTOTYPES #include "bccolors.h" #include "bcsignals.h" #include "bcwindowbase.h" #include "canvas.h" #include "clip.h" #include "condition.h" #include "edl.h" #include "maskautos.h" #include "maskauto.h" #include "mutex.h" #include "mwindow.h" #include "overlayframe.inc" #include "overlayframe.h" #include "playback3d.h" #include "pluginclient.h" #include "pluginvclient.h" #include "edlsession.h" #include "track.h" #include "transportque.inc" #include "vframe.h" #ifdef HAVE_GL #include #include #include #endif #include #include #include #define QQ(q)#q #define SS(s)QQ(s) // Shaders // These should be passed to VFrame::make_shader to construct shaders. // Can't hard code sampler2D #ifdef HAVE_GL static const char *yuv_to_rgb_frag = "uniform sampler2D tex;\n" "uniform mat3 yuv_to_rgb_matrix;\n" "uniform float yminf;\n" "void main()\n" "{\n" " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n" " yuva.rgb -= vec3(yminf, 0.5, 0.5);\n" " gl_FragColor = vec4(yuv_to_rgb_matrix * yuva.rgb, yuva.a);\n" "}\n"; static const char *yuva_to_yuv_frag = "uniform sampler2D tex;\n" "void main()\n" "{\n" " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n" " float a = yuva.a;\n" " float anti_a = 1.0 - a;\n" " yuva.r *= a;\n" " yuva.g = yuva.g * a + 0.5 * anti_a;\n" " yuva.b = yuva.b * a + 0.5 * anti_a;\n" " yuva.a = 1.0;\n" " gl_FragColor = yuva;\n" "}\n"; static const char *yuva_to_rgb_frag = "uniform sampler2D tex;\n" "uniform mat3 yuv_to_rgb_matrix;\n" "uniform float yminf;\n" "void main()\n" "{\n" " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n" " yuva.rgb -= vec3(yminf, 0.5, 0.5);\n" " yuva.rgb = yuv_to_rgb_matrix * yuva.rgb;\n" " yuva.rgb *= yuva.a;\n" " yuva.a = 1.0;\n" " gl_FragColor = yuva;\n" "}\n"; static const char *rgb_to_yuv_frag = "uniform sampler2D tex;\n" "uniform mat3 rgb_to_yuv_matrix;\n" "uniform float yminf;\n" "void main()\n" "{\n" " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n" " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n" " rgba.rgb += vec3(yminf, 0.5, 0.5);\n" " gl_FragColor = rgba;\n" "}\n"; static const char *rgba_to_rgb_frag = "uniform sampler2D tex;\n" "void main()\n" "{\n" " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n" " rgba.rgb *= rgba.a;\n" " rgba.a = 1.0;\n" " gl_FragColor = rgba;\n" "}\n"; static const char *rgba_to_yuv_frag = "uniform sampler2D tex;\n" "uniform mat3 rgb_to_yuv_matrix;\n" "uniform float yminf;\n" "void main()\n" "{\n" " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n" " rgba.rgb *= rgba.a;\n" " rgba.a = 1.0;\n" " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n" " rgba.rgb += vec3(yminf, 0.5, 0.5);\n" " gl_FragColor = rgba;\n" "}\n"; //static const char *rgba_to_rgb_flatten = // "void main() {\n" // " gl_FragColor.rgb *= gl_FragColor.a;\n" // " gl_FragColor.a = 1.0;\n" // "}\n"; #define GL_STD_BLEND(FN) \ static const char *blend_##FN##_frag = \ "uniform sampler2D tex2;\n" \ "uniform vec2 tex2_dimensions;\n" \ "uniform float alpha;\n" \ "void main() {\n" \ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" \ " vec4 result;\n" \ " result.rgb = " SS(COLOR_##FN(1.0, gl_FragColor.rgb, gl_FragColor.a, canvas.rgb, canvas.a)) ";\n" \ " result.a = " SS(ALPHA_##FN(1.0, gl_FragColor.a, canvas.a)) ";\n" \ " gl_FragColor = mix(canvas, result, alpha);\n" \ "}\n" #define GL_VEC_BLEND(FN) \ static const char *blend_##FN##_frag = \ "uniform sampler2D tex2;\n" \ "uniform vec2 tex2_dimensions;\n" \ "uniform float alpha;\n" \ "void main() {\n" \ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" \ " vec4 result;\n" \ " result.r = " SS(COLOR_##FN(1.0, gl_FragColor.r, gl_FragColor.a, canvas.r, canvas.a)) ";\n" \ " result.g = " SS(COLOR_##FN(1.0, gl_FragColor.g, gl_FragColor.a, canvas.g, canvas.a)) ";\n" \ " result.b = " SS(COLOR_##FN(1.0, gl_FragColor.b, gl_FragColor.a, canvas.b, canvas.a)) ";\n" \ " result.a = " SS(ALPHA_##FN(1.0, gl_FragColor.a, canvas.a)) ";\n" \ " result = clamp(result, 0.0, 1.0);\n" \ " gl_FragColor = mix(canvas, result, alpha);\n" \ "}\n" #undef mabs #define mabs abs #undef mmin #define mmin min #undef mmax #define mmax max #undef ZERO #define ZERO 0.0 #undef ONE #define ONE 1.0 #undef TWO #define TWO 2.0 // NORMAL static const char *blend_NORMAL_frag = "uniform sampler2D tex2;\n" "uniform vec2 tex2_dimensions;\n" "uniform float alpha;\n" "void main() {\n" " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" " vec4 result = mix(canvas, gl_FragColor, gl_FragColor.a);\n" " gl_FragColor = mix(canvas, result, alpha);\n" "}\n"; // REPLACE static const char *blend_REPLACE_frag = "uniform float alpha;\n" "void main() {\n" "}\n"; // ADDITION static const char *blend_ADDITION_frag = "uniform sampler2D tex2;\n" "uniform vec2 tex2_dimensions;\n" "uniform float alpha;\n" "void main() {\n" " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" " vec4 result = clamp(gl_FragColor + canvas, 0.0, 1.0);\n" " gl_FragColor = mix(canvas, result, alpha);\n" "}\n"; // SUBTRACT static const char *blend_SUBTRACT_frag = "uniform sampler2D tex2;\n" "uniform vec2 tex2_dimensions;\n" "uniform float alpha;\n" "void main() {\n" " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" " vec4 result = clamp(gl_FragColor - canvas, 0.0, 1.0);\n" " gl_FragColor = mix(canvas, result, alpha);\n" "}\n"; GL_STD_BLEND(MULTIPLY); GL_VEC_BLEND(DIVIDE); GL_VEC_BLEND(MAX); GL_VEC_BLEND(MIN); GL_VEC_BLEND(DARKEN); GL_VEC_BLEND(LIGHTEN); GL_STD_BLEND(DST); GL_STD_BLEND(DST_ATOP); GL_STD_BLEND(DST_IN); GL_STD_BLEND(DST_OUT); GL_STD_BLEND(DST_OVER); GL_STD_BLEND(SRC); GL_STD_BLEND(SRC_ATOP); GL_STD_BLEND(SRC_IN); GL_STD_BLEND(SRC_OUT); GL_STD_BLEND(SRC_OVER); GL_STD_BLEND(AND); GL_STD_BLEND(OR); GL_STD_BLEND(XOR); GL_VEC_BLEND(OVERLAY); GL_STD_BLEND(SCREEN); GL_VEC_BLEND(BURN); GL_VEC_BLEND(DODGE); GL_VEC_BLEND(HARDLIGHT); GL_VEC_BLEND(SOFTLIGHT); GL_VEC_BLEND(DIFFERENCE); static const char *read_texture_frag = "uniform sampler2D tex;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" "}\n"; static const char *in_vertex_frag = "#version 430 // vertex shader\n" "in vec3 in_pos;\n" "void main() {\n" " gl_Position = vec4(in_pos-vec3(0.5,0.5,0.), .5);\n" "}\n"; static const char *feather_frag = "#version 430\n" "layout(location=0) out vec4 color;\n" "uniform sampler2D tex;\n" "const int MAX = " SS(MAX_FEATHER) "+1;\n" "uniform float psf[MAX];\n" "uniform int n;\n" "uniform vec2 dxy;\n" "uniform vec2 twh;\n" "\n" "void main() {\n" " vec2 tc = gl_FragCoord.xy/textureSize(tex,0);\n" " color = texture(tex, tc);\n" " float c = color.r, f = c*psf[0];\n" " for( int i=1; i=0. ? 1. : -1.;\n" " float rr = r!=0. ? r : 1.;\n" " float rv = rr*v>=0. ? 1. : -1.;\n" " float vv = v>=0. ? 1.-v : 1.+v;\n" " float fg = rv>0. ? vv : 1.;\n" " float bg = rv>0. ? 1. : vv;\n" " float a = c*fg + (1.-c)*bg;\n" " if( iv*(color.a-a) > 0. ) color = vec4(a);\n" "}\n"; static const char *multiply_mask4_frag = "uniform sampler2D tex;\n" "uniform sampler2D tex1;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" " gl_FragColor.a *= texture2D(tex1, gl_TexCoord[0].st).r;\n" "}\n"; static const char *multiply_mask3_frag = "uniform sampler2D tex;\n" "uniform sampler2D tex1;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" " float a = texture2D(tex1, gl_TexCoord[0].st).r;\n" " gl_FragColor.rgb *= vec3(a, a, a);\n" "}\n"; static const char *multiply_yuvmask3_frag = "uniform sampler2D tex;\n" "uniform sampler2D tex1;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" " float a = texture2D(tex1, gl_TexCoord[0].st).r;\n" " gl_FragColor.gb -= vec2(0.5, 0.5);\n" " gl_FragColor.rgb *= vec3(a, a, a);\n" " gl_FragColor.gb += vec2(0.5, 0.5);\n" "}\n"; static const char *fade_rgba_frag = "uniform sampler2D tex;\n" "uniform float alpha;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" " gl_FragColor.a *= alpha;\n" "}\n"; static const char *fade_yuv_frag = "uniform sampler2D tex;\n" "uniform float alpha;\n" "void main()\n" "{\n" " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n" " gl_FragColor.r *= alpha;\n" " gl_FragColor.gb -= vec2(0.5, 0.5);\n" " gl_FragColor.g *= alpha;\n" " gl_FragColor.b *= alpha;\n" " gl_FragColor.gb += vec2(0.5, 0.5);\n" "}\n"; #endif Playback3DCommand::Playback3DCommand() : BC_SynchronousCommand() { canvas = 0; is_nested = 0; } void Playback3DCommand::copy_from(BC_SynchronousCommand *command) { Playback3DCommand *ptr = (Playback3DCommand*)command; this->canvas = ptr->canvas; this->is_cleared = ptr->is_cleared; this->in_x1 = ptr->in_x1; this->in_y1 = ptr->in_y1; this->in_x2 = ptr->in_x2; this->in_y2 = ptr->in_y2; this->out_x1 = ptr->out_x1; this->out_y1 = ptr->out_y1; this->out_x2 = ptr->out_x2; this->out_y2 = ptr->out_y2; this->alpha = ptr->alpha; this->mode = ptr->mode; this->interpolation_type = ptr->interpolation_type; this->input = ptr->input; this->start_position_project = ptr->start_position_project; this->keyframe_set = ptr->keyframe_set; this->keyframe = ptr->keyframe; this->default_auto = ptr->default_auto; this->plugin_client = ptr->plugin_client; this->want_texture = ptr->want_texture; this->is_nested = ptr->is_nested; this->dst_cmodel = ptr->dst_cmodel; BC_SynchronousCommand::copy_from(command); } Playback3D::Playback3D(MWindow *mwindow) : BC_Synchronous() { this->mwindow = mwindow; temp_texture = 0; } Playback3D::~Playback3D() { } BC_SynchronousCommand* Playback3D::new_command() { return new Playback3DCommand; } void Playback3D::handle_command(BC_SynchronousCommand *command) { //printf("Playback3D::handle_command 1 %d\n", command->command); switch(command->command) { case Playback3DCommand::WRITE_BUFFER: write_buffer_sync((Playback3DCommand*)command); break; case Playback3DCommand::CLEAR_OUTPUT: clear_output_sync((Playback3DCommand*)command); break; case Playback3DCommand::CLEAR_INPUT: clear_input_sync((Playback3DCommand*)command); break; case Playback3DCommand::DO_CAMERA: do_camera_sync((Playback3DCommand*)command); break; case Playback3DCommand::OVERLAY: overlay_sync((Playback3DCommand*)command); break; case Playback3DCommand::DO_FADE: do_fade_sync((Playback3DCommand*)command); break; case Playback3DCommand::DO_MASK: do_mask_sync((Playback3DCommand*)command); break; case Playback3DCommand::PLUGIN: run_plugin_sync((Playback3DCommand*)command); break; case Playback3DCommand::COPY_FROM: copy_from_sync((Playback3DCommand*)command); break; case Playback3DCommand::CONVERT_CMODEL: convert_cmodel_sync((Playback3DCommand*)command); break; // case Playback3DCommand::DRAW_REFRESH: // draw_refresh_sync((Playback3DCommand*)command); // break; } //printf("Playback3D::handle_command 10\n"); } void Playback3D::copy_from(Canvas *canvas, VFrame *dst, VFrame *src, int want_texture) { Playback3DCommand command; command.command = Playback3DCommand::COPY_FROM; command.canvas = canvas; command.frame = dst; command.input = src; command.want_texture = want_texture; send_command(&command); } void Playback3D::copy_from_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::copy_from_sync"); if( window ) { window->enable_opengl(); glFinish(); int w = command->input->get_w(); int h = command->input->get_h(); if(command->input->get_opengl_state() == VFrame::SCREEN && w == command->frame->get_w() && h == command->frame->get_h()) { // printf("Playback3D::copy_from_sync 1 %d %d %d %d %d\n", // command->input->get_w(), // command->input->get_h(), // command->frame->get_w(), // command->frame->get_h(), // command->frame->get_color_model()); #ifdef GLx4 // With NVidia at least if(w % 4) { printf("Playback3D::copy_from_sync: w=%d not supported because it is not divisible by 4.\n", w); } else #endif // Copy to texture if(command->want_texture) { //printf("Playback3D::copy_from_sync 1 dst=%p src=%p\n", command->frame, command->input); // Screen_to_texture requires the source pbuffer enabled. command->input->enable_opengl(); command->frame->screen_to_texture(); command->frame->set_opengl_state(VFrame::TEXTURE); } else // Copy to RAM { command->input->to_texture(); command->input->bind_texture(0); command->frame->enable_opengl(); command->frame->init_screen(); unsigned int shader = BC_CModels::is_yuv(command->input->get_color_model()) ? VFrame::make_shader(0, yuv_to_rgb_frag, 0) : 0; if( shader > 0 ) { glUseProgram(shader); int variable = glGetUniformLocation(shader, "tex"); glUniform1i(variable, 0); BC_GL_YUV_TO_RGB(shader); } else glUseProgram(0); command->input->draw_texture(1); command->frame->screen_to_ram(); glUseProgram(0); } } else { printf("Playback3D::copy_from_sync: invalid formats opengl_state=%d %dx%d -> %dx%d\n", command->input->get_opengl_state(), w, h, command->frame->get_w(), command->frame->get_h()); } } command->canvas->unlock_canvas(); #endif } // void Playback3D::draw_refresh(Canvas *canvas, // VFrame *frame, // float in_x1, // float in_y1, // float in_x2, // float in_y2, // float out_x1, // float out_y1, // float out_x2, // float out_y2) // { // Playback3DCommand command; // command.command = Playback3DCommand::DRAW_REFRESH; // command.canvas = canvas; // command.frame = frame; // command.in_x1 = in_x1; // command.in_y1 = in_y1; // command.in_x2 = in_x2; // command.in_y2 = in_y2; // command.out_x1 = out_x1; // command.out_y1 = out_y1; // command.out_x2 = out_x2; // command.out_y2 = out_y2; // send_command(&command); // } // // void Playback3D::draw_refresh_sync(Playback3DCommand *command) // { // #ifdef HAVE_GL // BC_WindowBase *window = // command->canvas->lock_canvas("Playback3D::draw_refresh_sync"); // if( window ) { // window->enable_opengl(); // // // Read output pbuffer back to RAM in project colormodel // // RGB 8bit is fastest for OpenGL to read back. // command->frame->reallocate(0, // 0, // 0, // 0, // command->frame->get_w(), // command->frame->get_h(), // BC_RGB888, // -1); // command->frame->screen_to_ram(); // // window->clear_box(0, // 0, // window->get_w(), // window->get_h()); // window->draw_vframe(command->frame, // (int)command->out_x1, // (int)command->out_y1, // (int)(command->out_x2 - command->out_x1), // (int)(command->out_y2 - command->out_y1), // (int)command->in_x1, // (int)command->in_y1, // (int)(command->in_x2 - command->in_x1), // (int)(command->in_y2 - command->in_y1), // 0); // // } // command->canvas->unlock_canvas(); // #endif // } void Playback3D::write_buffer(Canvas *canvas, VFrame *frame, float in_x1, float in_y1, float in_x2, float in_y2, float out_x1, float out_y1, float out_x2, float out_y2, int is_cleared) { Playback3DCommand command; command.command = Playback3DCommand::WRITE_BUFFER; command.canvas = canvas; command.frame = frame; command.in_x1 = in_x1; command.in_y1 = in_y1; command.in_x2 = in_x2; command.in_y2 = in_y2; command.out_x1 = out_x1; command.out_y1 = out_y1; command.out_x2 = out_x2; command.out_y2 = out_y2; command.is_cleared = is_cleared; send_command(&command); } void Playback3D::write_buffer_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::write_buffer_sync"); if( window ) { window->enable_opengl(); // Update hidden cursor window->update_video_cursor(); command->frame->enable_opengl(); command->frame->init_screen(); //printf("Playback3D::write_buffer_sync 1 %d\n", window->get_id()); int frame_state = command->frame->get_opengl_state(); if( frame_state != VFrame::TEXTURE ) command->frame->to_texture(); if( frame_state != VFrame::RAM ) { command->in_y1 = command->frame->get_h() - command->in_y1; command->in_y2 = command->frame->get_h() - command->in_y2; } window->enable_opengl(); draw_output(command, 1); command->frame->set_opengl_state(frame_state); } command->canvas->unlock_canvas(); #endif } void Playback3D::draw_output(Playback3DCommand *command, int flip_y) { #ifdef HAVE_GL int texture_id = command->frame->get_texture_id(); BC_WindowBase *window = command->canvas->get_canvas(); // printf("Playback3D::draw_output 1 texture_id=%d window=%p\n", // texture_id, // command->canvas->get_canvas()); // If virtual console is being used, everything in this function has // already been done except the page flip. if(texture_id >= 0) { canvas_w = window->get_w(); canvas_h = window->get_h(); VFrame::init_screen(canvas_w, canvas_h); int color_model = command->frame->get_color_model(); int is_yuv = BC_CModels::is_yuv(color_model); if(!command->is_cleared) { // If we get here, the virtual console was not used. int color = command->canvas->get_clear_color(); int r = (color>>16) & 0xff; // always rgb int g = (color>>8) & 0xff; int b = (color>>0) & 0xff; color_frame(command, r/255.f, g/255.f, b/255.f, 0.f); } // Texture // Undo any previous shader settings command->frame->bind_texture(0); // Convert colormodel unsigned int shader = is_yuv ? VFrame::make_shader(0, yuv_to_rgb_frag, 0) : 0; if( shader > 0 ) { glUseProgram(shader); // Set texture unit of the texture int variable = glGetUniformLocation(shader, "tex"); glUniform1i(variable, 0); BC_GL_YUV_TO_RGB(shader); } // if(BC_CModels::components(color_model) == 4) // { // glEnable(GL_BLEND); // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // } command->frame->draw_texture( command->in_x1, command->in_y1, command->in_x2, command->in_y2, command->out_x1, command->out_y1, command->out_x2, command->out_y2, flip_y); //printf("Playback3D::draw_output 2 %f,%f %f,%f -> %f,%f %f,%f\n", // command->in_x1, command->in_y1, command->in_x2, command->in_y2, // command->out_x1, command->out_y1, command->out_x2, command->out_y2); glUseProgram(0); command->canvas->get_canvas()->flip_opengl(); } #endif } void Playback3D::color_frame(Playback3DCommand *command, float r, float g, float b, float a) { #ifdef HAVE_GL glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); #endif } void Playback3D::clear_output(Canvas *canvas, VFrame *output) { Playback3DCommand command; command.command = Playback3DCommand::CLEAR_OUTPUT; command.canvas = canvas; command.frame = output; send_command(&command); } void Playback3D::clear_output_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::clear_output_sync"); if( window ) { // If we get here, the virtual console is being used. command->canvas->get_canvas()->enable_opengl(); int is_yuv = 0; int color = BLACK, alpha = 0; // Using pbuffer for refresh frame. if( command->frame ) { command->frame->enable_opengl(); command->frame->set_opengl_state(VFrame::SCREEN); color = command->frame->get_clear_color(); alpha = command->frame->get_clear_alpha(); int color_model = command->canvas->mwindow->edl->session->color_model; is_yuv = BC_CModels::is_yuv(color_model); } else color = command->canvas->get_clear_color(); int a = alpha; int r = (color>>16) & 0xff; int g = (color>>8) & 0xff; int b = (color>>0) & 0xff; if( is_yuv ) YUV::yuv.rgb_to_yuv_8(r, g, b); color_frame(command, r/255.f, g/255.f, b/255.f, a/255.f); } command->canvas->unlock_canvas(); #endif } void Playback3D::clear_input(Canvas *canvas, VFrame *frame) { Playback3DCommand command; command.command = Playback3DCommand::CLEAR_INPUT; command.canvas = canvas; command.frame = frame; send_command(&command); } void Playback3D::clear_input_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::clear_input_sync"); if( window ) { command->canvas->get_canvas()->enable_opengl(); command->frame->enable_opengl(); command->frame->clear_pbuffer(); command->frame->set_opengl_state(VFrame::SCREEN); } command->canvas->unlock_canvas(); #endif } void Playback3D::do_camera(Canvas *canvas, VFrame *output, VFrame *input, float in_x1, float in_y1, float in_x2, float in_y2, float out_x1, float out_y1, float out_x2, float out_y2) { Playback3DCommand command; command.command = Playback3DCommand::DO_CAMERA; command.canvas = canvas; command.input = input; command.frame = output; command.in_x1 = in_x1; command.in_y1 = in_y1; command.in_x2 = in_x2; command.in_y2 = in_y2; command.out_x1 = out_x1; command.out_y1 = out_y1; command.out_x2 = out_x2; command.out_y2 = out_y2; send_command(&command); } void Playback3D::do_camera_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::do_camera_sync"); if( window ) { command->canvas->get_canvas()->enable_opengl(); command->input->to_texture(); command->frame->enable_opengl(); command->frame->init_screen(); command->frame->clear_pbuffer(); command->input->bind_texture(0); // Must call draw_texture in input frame to get the texture coordinates right. // printf("Playback3D::do_camera_sync 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n", // command->in_x1, // command->in_y2, // command->in_x2, // command->in_y1, // command->out_x1, // (float)command->input->get_h() - command->out_y1, // command->out_x2, // (float)command->input->get_h() - command->out_y2); command->input->draw_texture( command->in_x1, command->in_y2, command->in_x2, command->in_y1, command->out_x1, (float)command->frame->get_h() - command->out_y1, command->out_x2, (float)command->frame->get_h() - command->out_y2); command->frame->set_opengl_state(VFrame::SCREEN); command->frame->screen_to_ram(); } command->canvas->unlock_canvas(); #endif } void Playback3D::overlay(Canvas *canvas, VFrame *input, float in_x1, float in_y1, float in_x2, float in_y2, float out_x1, float out_y1, float out_x2, float out_y2, float alpha, int mode, int interpolation_type, VFrame *output, int is_nested) { Playback3DCommand command; command.command = Playback3DCommand::OVERLAY; command.canvas = canvas; command.frame = output; command.input = input; command.in_x1 = in_x1; command.in_y1 = in_y1; command.in_x2 = in_x2; command.in_y2 = in_y2; command.out_x1 = out_x1; command.out_y1 = out_y1; command.out_x2 = out_x2; command.out_y2 = out_y2; command.alpha = alpha; command.mode = mode; command.interpolation_type = interpolation_type; command.is_nested = is_nested; send_command(&command); } void Playback3D::overlay_sync(Playback3DCommand *command) { #ifdef HAVE_GL // To do these operations, we need to copy the input buffer to a texture // and blend 2 textures in a shader static const char * const overlay_shaders[TRANSFER_TYPES] = { blend_NORMAL_frag, // TRANSFER_NORMAL blend_ADDITION_frag, // TRANSFER_ADDITION blend_SUBTRACT_frag, // TRANSFER_SUBTRACT blend_MULTIPLY_frag, // TRANSFER_MULTIPLY blend_DIVIDE_frag, // TRANSFER_DIVIDE blend_REPLACE_frag, // TRANSFER_REPLACE blend_MAX_frag, // TRANSFER_MAX blend_MIN_frag, // TRANSFER_MIN blend_DARKEN_frag, // TRANSFER_DARKEN blend_LIGHTEN_frag, // TRANSFER_LIGHTEN blend_DST_frag, // TRANSFER_DST blend_DST_ATOP_frag, // TRANSFER_DST_ATOP blend_DST_IN_frag, // TRANSFER_DST_IN blend_DST_OUT_frag, // TRANSFER_DST_OUT blend_DST_OVER_frag, // TRANSFER_DST_OVER blend_SRC_frag, // TRANSFER_SRC blend_SRC_ATOP_frag, // TRANSFER_SRC_ATOP blend_SRC_IN_frag, // TRANSFER_SRC_IN blend_SRC_OUT_frag, // TRANSFER_SRC_OUT blend_SRC_OVER_frag, // TRANSFER_SRC_OVER blend_AND_frag, // TRANSFER_AND blend_OR_frag, // TRANSFER_OR blend_XOR_frag, // TRANSFER_XOR blend_OVERLAY_frag, // TRANSFER_OVERLAY blend_SCREEN_frag, // TRANSFER_SCREEN blend_BURN_frag, // TRANSFER_BURN blend_DODGE_frag, // TRANSFER_DODGE blend_HARDLIGHT_frag, // TRANSFER_HARDLIGHT blend_SOFTLIGHT_frag, // TRANSFER_SOFTLIGHT blend_DIFFERENCE_frag, // TRANSFER_DIFFERENCE }; BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::overlay_sync"); if( window ) { // Make sure OpenGL is enabled first. window->enable_opengl(); window->update_video_cursor(); glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); //printf("Playback3D::overlay_sync 1 %d\n", command->input->get_opengl_state()); switch( command->input->get_opengl_state() ) { // Upload texture and composite to screen case VFrame::RAM: command->input->to_texture(); break; // Just composite texture to screen case VFrame::TEXTURE: break; // read from PBuffer to texture, then composite texture to screen case VFrame::SCREEN: command->input->enable_opengl(); command->input->screen_to_texture(); break; default: printf("Playback3D::overlay_sync unknown state\n"); break; } if(command->frame) { // Render to PBuffer command->frame->enable_opengl(); command->frame->set_opengl_state(VFrame::SCREEN); canvas_w = command->frame->get_w(); canvas_h = command->frame->get_h(); } else { // Render to canvas window->enable_opengl(); canvas_w = window->get_w(); canvas_h = window->get_h(); } const char *shader_stack[16]; memset(shader_stack,0, sizeof(shader_stack)); int total_shaders = 0, need_matrix = 0; VFrame::init_screen(canvas_w, canvas_h); // Enable texture command->input->bind_texture(0); // Convert colormodel to RGB if not nested. // The color model setting in the output frame is ignored. // if( command->is_nested <= 0 && // not nested // BC_CModels::is_yuv(command->input->get_color_model()) ) { // need_matrix = 1; // shader_stack[total_shaders++] = yuv_to_rgb_frag; // } // get the shaders #define add_shader(s) \ if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; \ shader_stack[total_shaders++] = s switch(command->mode) { case TRANSFER_REPLACE: // This requires overlaying an alpha multiplied image on a black screen. if( command->input->get_texture_components() != 4 ) break; add_shader(overlay_shaders[command->mode]); break; default: enable_overlay_texture(command); add_shader(overlay_shaders[command->mode]); break; } // if to flatten alpha // if( command->is_nested < 0 ) { // switch(command->input->get_color_model()) { //// yuv has already been converted to rgb // case BC_YUVA8888: // case BC_RGBA_FLOAT: // case BC_RGBA8888: // add_shader(rgba_to_rgb_flatten); // break; // } // } // run the shaders add_shader(0); unsigned int shader = !shader_stack[0] ? 0 : VFrame::make_shader(shader_stack); if( shader > 0 ) { glUseProgram(shader); if( need_matrix ) BC_GL_YUV_TO_RGB(shader); // Set texture unit of the texture glUniform1i(glGetUniformLocation(shader, "tex"), 0); // Set texture unit of the temp texture glUniform1i(glGetUniformLocation(shader, "tex2"), 1); // Set alpha int variable = glGetUniformLocation(shader, "alpha"); glUniform1f(variable, command->alpha); // Set dimensions of the temp texture if(temp_texture) glUniform2f(glGetUniformLocation(shader, "tex2_dimensions"), (float)temp_texture->get_texture_w(), (float)temp_texture->get_texture_h()); } else glUseProgram(0); //printf("Playback3D::overlay_sync %f %f %f %f %f %f %f %f\n", // command->in_x1, command->in_y1, command->in_x2, command->in_y2, // command->out_x1, command->out_y1, command->out_x2, command->out_y2); command->input->draw_texture( command->in_x1, command->in_y1, command->in_x2, command->in_y2, command->out_x1, command->out_y1, command->out_x2, command->out_y2, !command->is_nested); glUseProgram(0); // Delete temp texture if(temp_texture) { delete temp_texture; temp_texture = 0; glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); } command->canvas->unlock_canvas(); #endif } void Playback3D::enable_overlay_texture(Playback3DCommand *command) { #ifdef HAVE_GL glDisable(GL_BLEND); glActiveTexture(GL_TEXTURE1); BC_Texture::new_texture(&temp_texture, canvas_w, canvas_h, command->input->get_color_model()); temp_texture->bind(1); // Read canvas into texture glReadBuffer(GL_BACK); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, canvas_w, canvas_h); #endif } void Playback3D::do_mask(Canvas *canvas, VFrame *output, int64_t start_position_project, MaskAutos *keyframe_set, MaskAuto *keyframe, MaskAuto *default_auto) { Playback3DCommand command; command.command = Playback3DCommand::DO_MASK; command.canvas = canvas; command.frame = output; command.start_position_project = start_position_project; command.keyframe_set = keyframe_set; command.keyframe = keyframe; command.default_auto = default_auto; send_command(&command); } void Playback3D::draw_spots(MaskSpots &spots, int ix1,int iy1, int ix2,int iy2) { int x1 = iy1 < iy2 ? ix1 : ix2; int y1 = iy1 < iy2 ? iy1 : iy2; int x2 = iy1 < iy2 ? ix2 : ix1; int y2 = iy1 < iy2 ? iy2 : iy1; int x = x1, y = y1; int dx = x2-x1, dy = y2-y1; int dx2 = 2*dx, dy2 = 2*dy; if( dx < 0 ) dx = -dx; int m = dx > dy ? dx : dy, n = m; if( dy >= dx ) { if( dx2 >= 0 ) do { /* +Y, +X */ spots.append(x, y++); if( (m -= dx2) < 0 ) { m += dy2; ++x; } } while( --n >= 0 ); else do { /* +Y, -X */ spots.append(x, y++); if( (m += dx2) < 0 ) { m += dy2; --x; } } while( --n >= 0 ); } else { if( dx2 >= 0 ) do { /* +X, +Y */ spots.append(x++, y); if( (m -= dy2) < 0 ) { m += dx2; ++y; } } while( --n >= 0 ); else do { /* -X, +Y */ spots.append(x--, y); if( (m -= dy2) < 0 ) { m -= dx2; ++y; } } while( --n >= 0 ); } } #ifdef HAVE_GL class fb_texture : public BC_Texture { public: fb_texture(int w, int h, int colormodel); ~fb_texture(); void bind(int texture_unit); void read_screen(int x, int y, int w, int h); void set_output_texture(); void unset_output_texture(); GLuint fb, rb; }; fb_texture::fb_texture(int w, int h, int colormodel) : BC_Texture(w, h, colormodel) { fb = 0; rb = 0; // glGenRenderbuffers(1, &rb); // glBindRenderbuffer(GL_RENDERBUFFER, rb); // glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, get_texture_w(), get_texture_w()); glGenFramebuffers(1, &fb); glBindFramebuffer(GL_FRAMEBUFFER, fb); // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb); } fb_texture::~fb_texture() { glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, (GLuint *)&fb); // glBindRenderbuffer(GL_RENDERBUFFER, 0); // glGenRenderbuffers(1, &rb); } void fb_texture::bind(int texture_unit) { glBindFramebuffer(GL_FRAMEBUFFER, fb); // glBindRenderbuffer(GL_RENDERBUFFER, rb); BC_Texture::bind(texture_unit); } void fb_texture::read_screen(int x, int y, int w, int h) { bind(1); glBindFramebuffer(GL_FRAMEBUFFER, 0); glReadBuffer(GL_BACK); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, x,y, w,h); } void fb_texture::set_output_texture() { GLenum at = GL_COLOR_ATTACHMENT0; glFramebufferTexture(GL_FRAMEBUFFER, at, get_texture_id(), 0); GLenum dbo[1] = { at, }; // bind layout(location=0) out vec4 color; glDrawBuffers(1, dbo); int ret = glCheckFramebufferStatus(GL_FRAMEBUFFER); if( ret != GL_FRAMEBUFFER_COMPLETE ) { printf("glDrawBuffer error 0x%04x\n", ret); return; } } void fb_texture::unset_output_texture() { glDrawBuffers(0, 0); int at = GL_COLOR_ATTACHMENT0; glFramebufferTexture(GL_FRAMEBUFFER, at, 0, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0); glDisable(GL_TEXTURE_2D); } class zglTessData : public ArrayList { public: zglTessData() { set_array_delete(); } ~zglTessData() { remove_all_objects(); } }; static void combineData(GLdouble coords[3], GLdouble *vertex_data[4], GLfloat weight[4], GLdouble **outData, void *data) { zglTessData *invented = (zglTessData *)data; GLdouble *vertex = new double[6]; invented->append(vertex); vertex[0] = coords[0]; vertex[1] = coords[1]; vertex[2] = coords[2]; for( int i=3; i<6; ++i ) { GLdouble v = 0; for( int k=0; k<4; ++k ) { if( !weight[k] || !vertex_data[k] ) continue; v += weight[k] * vertex_data[k][i]; } vertex[i] = v; } *outData = vertex; } // dbug static void zglBegin(GLenum mode) { glBegin(mode); } static void zglEnd() { glEnd(); } static void zglVertex(const GLdouble *v) { glVertex3dv(v); } #endif void Playback3D::do_mask_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::do_mask_sync"); if( window ) { window->enable_opengl(); switch( command->frame->get_opengl_state() ) { case VFrame::RAM: // upload frame to the texture command->frame->to_texture(); break; case VFrame::SCREEN: // Read back from PBuffer // Bind context to pbuffer command->frame->enable_opengl(); command->frame->screen_to_texture(); break; } // Initialize coordinate system command->frame->enable_opengl(); command->frame->init_screen(); int color_model = command->frame->get_color_model(); int w = command->frame->get_w(); int h = command->frame->get_h(); MaskEdges edges; float faders[SUBMASKS], feathers[SUBMASKS], cc = 1; MaskPoints point_set[SUBMASKS]; // Draw every submask as a new polygon int total_submasks = command->keyframe_set->total_submasks( command->start_position_project, PLAY_FORWARD); int show_mask = command->keyframe_set->track->masks; for(int k = 0; k < total_submasks; k++) { MaskPoints &points = point_set[k]; command->keyframe_set->get_points(&points, k, command->start_position_project, PLAY_FORWARD); float fader = command->keyframe_set->get_fader( command->start_position_project, k, PLAY_FORWARD); float v = fader/100.; faders[k] = v; float feather = command->keyframe_set->get_feather( command->start_position_project, k, PLAY_FORWARD); feathers[k] = feather; MaskEdge &edge = *edges.append(new MaskEdge()); if( !v || !((show_mask>>k) & 1) || !points.size() ) continue; edge.load(point_set[k], h); if( v >= 0 ) continue; float vv = 1 + v; if( cc > vv ) cc = vv; } fb_texture *mask = new fb_texture(w, h, color_model); mask->set_output_texture(); glClearColor(cc, cc, cc, cc); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); mask->unset_output_texture(); unsigned int feather_shader = VFrame::make_shader(0, in_vertex_frag, feather_frag, 0); unsigned int max_shader = VFrame::make_shader(0, in_vertex_frag, max_frag, 0); if( feather_shader && max_shader ) { fb_texture *in = new fb_texture(w, h, color_model); fb_texture *out = new fb_texture(w, h, color_model); float tw = 1./out->get_texture_w(), th = 1./out->get_texture_h(); float tw1 = (w-1)*tw, th1 = (h-1)*th; for(int k = 0; k < total_submasks; k++) { MaskEdge &edge = *edges[k]; if( edge.size() < 3 ) continue; float r = feathers[k], v = faders[k]; glBindFramebuffer(GL_FRAMEBUFFER, 0); glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); float b = r>=0 ? 0. : 1.; float f = r>=0 ? 1. : 0.; glClearColor(b, b, b, b); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor4f(f, f, f, f); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); int display_list = glGenLists(1); #if 0 glNewList(display_list, GL_COMPILE); glBegin(GL_POLYGON); MaskCoord *c = &edge[0]; for( int i=edge.size(); --i>=0; ++c ) glVertex2f(c->x, c->y); glEnd(); glEndList(); glCallList(display_list); #else { zglTessData invented; GLUtesselator *tess = gluNewTess(); gluTessProperty(tess, GLU_TESS_TOLERANCE, 0.5); gluTessCallback(tess, GLU_TESS_VERTEX,(GLvoid (*)()) &zglVertex); gluTessCallback(tess, GLU_TESS_BEGIN,(GLvoid (*)()) &zglBegin); gluTessCallback(tess, GLU_TESS_END,(GLvoid (*)()) &zglEnd); gluTessCallback(tess, GLU_TESS_COMBINE_DATA,(GLvoid (*)()) &combineData); glNewList(display_list, GL_COMPILE); gluTessBeginPolygon(tess, &invented); gluTessBeginContour(tess); MaskCoord *c = &edge[0]; for( int i=edge.size(); --i>=0; ++c ) gluTessVertex(tess, (GLdouble *)c, c); gluTessEndContour(tess); gluTessEndPolygon(tess); glEndList(); glCallList(display_list); gluDeleteTess(tess); } #endif glDeleteLists(1, display_list); in->read_screen(0,0, w,h); //in->write_tex("/tmp/in0.ppm"); if( r ) { double sig2 = -log(255.0)/(r*r); int n = abs((int)r) + 1; if( n > MAX_FEATHER+1 ) n = MAX_FEATHER+1; float psf[n]; // point spot fn for( int i=0; ibind(0); out->set_output_texture(); out->draw_texture(0,0, w,h, 0,0, w,h); out->unset_output_texture(); //out->write_tex("/tmp/out1.ppm"); fb_texture *t = in; in = out; out = t; glUniform2f(glGetUniformLocation(feather_shader, "dxy"), 0., th); in->bind(0); out->set_output_texture(); out->draw_texture(0,0, w,h, 0,0, w,h); out->unset_output_texture(); //out->write_tex("/tmp/out2.ppm"); glUseProgram(0); t = in; in = out; out = t; } glUseProgram(max_shader); in->bind(0); //in->write_tex("/tmp/in1.ppm"); //mask->write_tex("/tmp/mask1.ppm"); mask->bind(1); glUniform1i(glGetUniformLocation(max_shader, "tex"), 0); glUniform1i(glGetUniformLocation(max_shader, "tex1"), 1); glUniform1f(glGetUniformLocation(max_shader, "r"), r); glUniform1f(glGetUniformLocation(max_shader, "v"), v); glViewport(0,0, w,h); out->set_output_texture(); out->draw_texture(0,0, w,h, 0,0, w,h); out->unset_output_texture(); glUseProgram(0); fb_texture *t = mask; mask = out; out = t; //mask->write_tex("/tmp/mask2.ppm"); } delete in; delete out; } const char *alpha_shader = BC_CModels::has_alpha(color_model) ? multiply_mask4_frag : !BC_CModels::is_yuv(color_model) ? multiply_mask3_frag : multiply_yuvmask3_frag; unsigned int shader = VFrame::make_shader(0, alpha_shader, 0); glUseProgram(shader); if( shader > 0 ) { command->frame->bind_texture(0); mask->BC_Texture::bind(1); glUniform1i(glGetUniformLocation(shader, "tex"), 0); glUniform1i(glGetUniformLocation(shader, "tex1"), 1); } command->frame->draw_texture(); command->frame->set_opengl_state(VFrame::SCREEN); glUseProgram(0); delete mask; glColor4f(1, 1, 1, 1); glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); window->enable_opengl(); } command->canvas->unlock_canvas(); #endif } void Playback3D::convert_cmodel(Canvas *canvas, VFrame *output, int dst_cmodel) { // Do nothing if colormodels are equivalent in OpenGL & the image is in hardware. int src_cmodel = output->get_color_model(); if( (output->get_opengl_state() == VFrame::TEXTURE || output->get_opengl_state() == VFrame::SCREEN) && // OpenGL has no floating point. ( (src_cmodel == BC_RGB888 && dst_cmodel == BC_RGB_FLOAT) || (src_cmodel == BC_RGBA8888 && dst_cmodel == BC_RGBA_FLOAT) || (src_cmodel == BC_RGB_FLOAT && dst_cmodel == BC_RGB888) || (src_cmodel == BC_RGBA_FLOAT && dst_cmodel == BC_RGBA8888) || // OpenGL sets alpha to 1 on import (src_cmodel == BC_RGB888 && dst_cmodel == BC_RGBA8888) || (src_cmodel == BC_YUV888 && dst_cmodel == BC_YUVA8888) || (src_cmodel == BC_RGB_FLOAT && dst_cmodel == BC_RGBA_FLOAT) ) ) return; Playback3DCommand command; command.command = Playback3DCommand::CONVERT_CMODEL; command.canvas = canvas; command.frame = output; command.dst_cmodel = dst_cmodel; send_command(&command); } void Playback3D::convert_cmodel_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::convert_cmodel_sync"); if( window ) { window->enable_opengl(); // Import into hardware command->frame->enable_opengl(); command->frame->init_screen(); command->frame->to_texture(); // Colormodel permutation int src_cmodel = command->frame->get_color_model(); int dst_cmodel = command->dst_cmodel; typedef struct { int src, dst, typ; const char *shader; } cmodel_shader_table_t; enum { rgb_to_rgb, rgb_to_yuv, yuv_to_rgb, yuv_to_yuv, }; int type = -1; static cmodel_shader_table_t cmodel_shader_table[] = { { BC_RGB888, BC_YUV888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_RGB888, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_RGBA8888, BC_RGB888, rgb_to_rgb, rgba_to_rgb_frag }, { BC_RGBA8888, BC_RGB_FLOAT, rgb_to_rgb, rgba_to_rgb_frag }, { BC_RGBA8888, BC_YUV888, rgb_to_yuv, rgba_to_yuv_frag }, { BC_RGBA8888, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_RGB_FLOAT, BC_YUV888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_RGB_FLOAT, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_RGBA_FLOAT,BC_RGB888, rgb_to_rgb, rgba_to_rgb_frag }, { BC_RGBA_FLOAT,BC_RGB_FLOAT, rgb_to_rgb, rgba_to_rgb_frag }, { BC_RGBA_FLOAT,BC_YUV888, rgb_to_yuv, rgba_to_yuv_frag }, { BC_RGBA_FLOAT,BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag }, { BC_YUV888, BC_RGB888, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUV888, BC_RGBA8888, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUV888, BC_RGB_FLOAT, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUV888, BC_RGBA_FLOAT, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUVA8888, BC_RGB888, yuv_to_rgb, yuva_to_rgb_frag }, { BC_YUVA8888, BC_RGBA8888, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUVA8888, BC_RGB_FLOAT, yuv_to_rgb, yuva_to_rgb_frag }, { BC_YUVA8888, BC_RGBA_FLOAT, yuv_to_rgb, yuv_to_rgb_frag }, { BC_YUVA8888, BC_YUV888, yuv_to_yuv, yuva_to_yuv_frag }, }; const char *shader = 0; int table_size = sizeof(cmodel_shader_table) / sizeof(cmodel_shader_table_t); for( int i=0; iframe->get_color_model(), // command->dst_cmodel, // shader); const char *shader_stack[9]; memset(shader_stack,0, sizeof(shader_stack)); int current_shader = 0; if( shader ) { //printf("Playback3D::convert_cmodel_sync %d\n", __LINE__); shader_stack[current_shader++] = shader; shader_stack[current_shader] = 0; unsigned int shader_id = VFrame::make_shader(shader_stack); command->frame->bind_texture(0); glUseProgram(shader_id); glUniform1i(glGetUniformLocation(shader_id, "tex"), 0); switch( type ) { case rgb_to_yuv: BC_GL_RGB_TO_YUV(shader_id); break; case yuv_to_rgb: BC_GL_YUV_TO_RGB(shader_id); break; } command->frame->draw_texture(); if(shader) glUseProgram(0); command->frame->set_opengl_state(VFrame::SCREEN); } } command->canvas->unlock_canvas(); #endif // HAVE_GL } void Playback3D::do_fade(Canvas *canvas, VFrame *frame, float fade) { Playback3DCommand command; command.command = Playback3DCommand::DO_FADE; command.canvas = canvas; command.frame = frame; command.alpha = fade; send_command(&command); } void Playback3D::do_fade_sync(Playback3DCommand *command) { #ifdef HAVE_GL BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::do_fade_sync"); if( window ) { window->enable_opengl(); switch( command->frame->get_opengl_state() ) { case VFrame::RAM: command->frame->to_texture(); break; case VFrame::SCREEN: // Read back from PBuffer // Bind context to pbuffer command->frame->enable_opengl(); command->frame->screen_to_texture(); break; } command->frame->enable_opengl(); command->frame->init_screen(); command->frame->bind_texture(0); // glClearColor(0.0, 0.0, 0.0, 0.0); // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDisable(GL_BLEND); unsigned int frag_shader = 0; switch(command->frame->get_color_model()) { // For the alpha colormodels, the native function seems to multiply the // components by the alpha instead of just the alpha. case BC_RGBA8888: case BC_RGBA_FLOAT: case BC_YUVA8888: frag_shader = VFrame::make_shader(0, fade_rgba_frag, 0); break; case BC_RGB888: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ZERO); glColor4f(command->alpha, command->alpha, command->alpha, 1); break; case BC_YUV888: frag_shader = VFrame::make_shader(0, fade_yuv_frag, 0); break; } if( frag_shader ) { glUseProgram(frag_shader); int variable; if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0) glUniform1i(variable, 0); if((variable = glGetUniformLocation(frag_shader, "alpha")) >= 0) glUniform1f(variable, command->alpha); } command->frame->draw_texture(); command->frame->set_opengl_state(VFrame::SCREEN); if(frag_shader) { glUseProgram(0); } glColor4f(1, 1, 1, 1); glDisable(GL_BLEND); } command->canvas->unlock_canvas(); #endif } int Playback3D::run_plugin(Canvas *canvas, PluginClient *client) { Playback3DCommand command; command.command = Playback3DCommand::PLUGIN; command.canvas = canvas; command.plugin_client = client; return send_command(&command); } void Playback3D::run_plugin_sync(Playback3DCommand *command) { BC_WindowBase *window = command->canvas->lock_canvas("Playback3D::run_plugin_sync"); if( window ) { window->enable_opengl(); command->result = ((PluginVClient*)command->plugin_client)->handle_opengl(); } command->canvas->unlock_canvas(); }