4 * Copyright (C) 2009 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 #define GL_GLEXT_PROTOTYPES
25 #include "bcsignals.h"
26 #include "bcwindowbase.h"
29 #include "condition.h"
31 #include "maskautos.h"
35 #include "overlayframe.inc"
36 #include "overlayframe.h"
37 #include "playback3d.h"
38 #include "pluginclient.h"
39 #include "pluginvclient.h"
40 #include "edlsession.h"
41 #include "transportque.inc"
59 // These should be passed to VFrame::make_shader to construct shaders.
60 // Can't hard code sampler2D
64 static const char *yuv_to_rgb_frag =
65 "uniform sampler2D tex;\n"
66 "uniform mat3 yuv_to_rgb_matrix;\n"
67 "uniform float yminf;\n"
70 " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
71 " yuva.rgb -= vec3(yminf, 0.5, 0.5);\n"
72 " gl_FragColor = vec4(yuv_to_rgb_matrix * yuva.rgb, yuva.a);\n"
75 static const char *yuva_to_yuv_frag =
76 "uniform sampler2D tex;\n"
79 " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
80 " float a = yuva.a;\n"
81 " float anti_a = 1.0 - a;\n"
83 " yuva.g = yuva.g * a + 0.5 * anti_a;\n"
84 " yuva.b = yuva.b * a + 0.5 * anti_a;\n"
86 " gl_FragColor = yuva;\n"
89 static const char *yuva_to_rgb_frag =
90 "uniform sampler2D tex;\n"
91 "uniform mat3 yuv_to_rgb_matrix;\n"
92 "uniform float yminf;\n"
95 " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
96 " yuva.rgb -= vec3(yminf, 0.5, 0.5);\n"
97 " yuva.rgb = yuv_to_rgb_matrix * yuva.rgb;\n"
98 " yuva.rgb *= yuva.a;\n"
100 " gl_FragColor = yuva;\n"
103 static const char *rgb_to_yuv_frag =
104 "uniform sampler2D tex;\n"
105 "uniform mat3 rgb_to_yuv_matrix;\n"
106 "uniform float yminf;\n"
109 " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
110 " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n"
111 " rgba.rgb += vec3(yminf, 0.5, 0.5);\n"
112 " gl_FragColor = rgba;\n"
116 static const char *rgba_to_rgb_frag =
117 "uniform sampler2D tex;\n"
120 " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
121 " rgba.rgb *= rgba.a;\n"
123 " gl_FragColor = rgba;\n"
126 static const char *rgba_to_yuv_frag =
127 "uniform sampler2D tex;\n"
128 "uniform mat3 rgb_to_yuv_matrix;\n"
129 "uniform float yminf;\n"
132 " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
133 " rgba.rgb *= rgba.a;\n"
135 " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n"
136 " rgba.rgb += vec3(yminf, 0.5, 0.5);\n"
137 " gl_FragColor = rgba;\n"
140 //static const char *rgba_to_rgb_flatten =
142 // " gl_FragColor.rgb *= gl_FragColor.a;\n"
143 // " gl_FragColor.a = 1.0;\n"
146 #define GL_STD_BLEND(FN) \
147 static const char *blend_##FN##_frag = \
148 "uniform sampler2D tex2;\n" \
149 "uniform vec2 tex2_dimensions;\n" \
150 "uniform float alpha;\n" \
152 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" \
154 " result.rgb = " SS(COLOR_##FN(1.0, gl_FragColor.rgb, gl_FragColor.a, canvas.rgb, canvas.a)) ";\n" \
155 " result.a = " SS(ALPHA_##FN(1.0, gl_FragColor.a, canvas.a)) ";\n" \
156 " gl_FragColor = mix(canvas, result, alpha);\n" \
159 #define GL_VEC_BLEND(FN) \
160 static const char *blend_##FN##_frag = \
161 "uniform sampler2D tex2;\n" \
162 "uniform vec2 tex2_dimensions;\n" \
163 "uniform float alpha;\n" \
165 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n" \
167 " result.r = " SS(COLOR_##FN(1.0, gl_FragColor.r, gl_FragColor.a, canvas.r, canvas.a)) ";\n" \
168 " result.g = " SS(COLOR_##FN(1.0, gl_FragColor.g, gl_FragColor.a, canvas.g, canvas.a)) ";\n" \
169 " result.b = " SS(COLOR_##FN(1.0, gl_FragColor.b, gl_FragColor.a, canvas.b, canvas.a)) ";\n" \
170 " result.a = " SS(ALPHA_##FN(1.0, gl_FragColor.a, canvas.a)) ";\n" \
171 " result = clamp(result, 0.0, 1.0);\n" \
172 " gl_FragColor = mix(canvas, result, alpha);\n" \
190 static const char *blend_NORMAL_frag =
191 "uniform sampler2D tex2;\n"
192 "uniform vec2 tex2_dimensions;\n"
193 "uniform float alpha;\n"
195 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
196 " vec4 result = mix(canvas, gl_FragColor, gl_FragColor.a);\n"
197 " gl_FragColor = mix(canvas, result, alpha);\n"
201 static const char *blend_REPLACE_frag =
202 "uniform float alpha;\n"
207 static const char *blend_ADDITION_frag =
208 "uniform sampler2D tex2;\n"
209 "uniform vec2 tex2_dimensions;\n"
210 "uniform float alpha;\n"
212 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
213 " vec4 result = clamp(gl_FragColor + canvas, 0.0, 1.0);\n"
214 " gl_FragColor = mix(canvas, result, alpha);\n"
218 static const char *blend_SUBTRACT_frag =
219 "uniform sampler2D tex2;\n"
220 "uniform vec2 tex2_dimensions;\n"
221 "uniform float alpha;\n"
223 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
224 " vec4 result = clamp(gl_FragColor - canvas, 0.0, 1.0);\n"
225 " gl_FragColor = mix(canvas, result, alpha);\n"
228 GL_STD_BLEND(MULTIPLY);
229 GL_VEC_BLEND(DIVIDE);
232 GL_VEC_BLEND(DARKEN);
233 GL_VEC_BLEND(LIGHTEN);
235 GL_STD_BLEND(DST_ATOP);
236 GL_STD_BLEND(DST_IN);
237 GL_STD_BLEND(DST_OUT);
238 GL_STD_BLEND(DST_OVER);
240 GL_STD_BLEND(SRC_ATOP);
241 GL_STD_BLEND(SRC_IN);
242 GL_STD_BLEND(SRC_OUT);
243 GL_STD_BLEND(SRC_OVER);
247 GL_VEC_BLEND(OVERLAY);
248 GL_STD_BLEND(SCREEN);
251 GL_VEC_BLEND(HARDLIGHT);
252 GL_VEC_BLEND(SOFTLIGHT);
253 GL_VEC_BLEND(DIFFERENCE);
255 static const char *read_texture_frag =
256 "uniform sampler2D tex;\n"
259 " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
262 static const char *in_vertex_frag =
263 "#version 430 // vertex shader\n"
266 " gl_Position = vec4(in_pos-vec3(0.5,0.5,0.), .5);\n"
269 static const char *feather_frag =
271 "layout(location=0) out vec4 color;\n"
272 "uniform sampler2D tex;\n"
273 // apparently, only doubles index properly in shared buffers
274 "buffer buf { dvec2 points[]; };\n"
278 " vec2 tex_st = gl_FragCoord.xy/textureSize(tex,0);\n"
279 " color = texture(tex, tex_st);\n"
280 " if( r==0. ) return;\n"
281 " float rv = r*v>0. ? 1 : -1;\n"
282 " float rr = r*r, dr = 1./rr;\n"
283 " float vv = v>=0 ? 1.-v : 1.+v;\n"
284 " float fg = rv>=0 ? vv : 1.;\n"
285 " float bg = rv>=0 ? 1. : vv;\n"
286 " int len = points.length();\n"
287 " for( int i=0; i<len; ++i ) {\n"
288 " float dx = float(points[i].x) - gl_FragCoord.x;\n"
289 " float dy = float(points[i].y) - gl_FragCoord.y;\n"
290 " float dd = dx*dx + dy*dy;\n"
291 " if( dd >= rr ) continue;\n"
292 " float d = dd*dr;\n"
293 " float a = (1.-d)*fg + d*bg;\n"
294 " if( rv*(color.a-a) > 0 ) color = vec4(a);\n"
298 static const char *alpha_frag =
299 "uniform sampler2D tex;\n"
300 "uniform sampler2D tex2;\n"
301 "uniform vec2 tex2_dimensions;\n"
303 " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
304 " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
305 " gl_FragColor.a = canvas.a;\n"
308 static const char *fade_rgba_frag =
309 "uniform sampler2D tex;\n"
310 "uniform float alpha;\n"
313 " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
314 " gl_FragColor.a *= alpha;\n"
317 static const char *fade_yuv_frag =
318 "uniform sampler2D tex;\n"
319 "uniform float alpha;\n"
322 " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
323 " gl_FragColor.r *= alpha;\n"
324 " gl_FragColor.gb -= vec2(0.5, 0.5);\n"
325 " gl_FragColor.g *= alpha;\n"
326 " gl_FragColor.b *= alpha;\n"
327 " gl_FragColor.gb += vec2(0.5, 0.5);\n"
333 Playback3DCommand::Playback3DCommand()
334 : BC_SynchronousCommand()
340 void Playback3DCommand::copy_from(BC_SynchronousCommand *command)
342 Playback3DCommand *ptr = (Playback3DCommand*)command;
343 this->canvas = ptr->canvas;
344 this->is_cleared = ptr->is_cleared;
346 this->in_x1 = ptr->in_x1;
347 this->in_y1 = ptr->in_y1;
348 this->in_x2 = ptr->in_x2;
349 this->in_y2 = ptr->in_y2;
350 this->out_x1 = ptr->out_x1;
351 this->out_y1 = ptr->out_y1;
352 this->out_x2 = ptr->out_x2;
353 this->out_y2 = ptr->out_y2;
354 this->alpha = ptr->alpha;
355 this->mode = ptr->mode;
356 this->interpolation_type = ptr->interpolation_type;
358 this->input = ptr->input;
359 this->start_position_project = ptr->start_position_project;
360 this->keyframe_set = ptr->keyframe_set;
361 this->keyframe = ptr->keyframe;
362 this->default_auto = ptr->default_auto;
363 this->plugin_client = ptr->plugin_client;
364 this->want_texture = ptr->want_texture;
365 this->is_nested = ptr->is_nested;
366 this->dst_cmodel = ptr->dst_cmodel;
368 BC_SynchronousCommand::copy_from(command);
373 static void GLAPIENTRY glDebugCallback(GLenum source, GLenum type,
374 GLuint id, GLenum severity, GLsizei length, const GLchar* message,
375 const void* userParam)
377 fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
378 ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
379 type, severity, message );
383 Playback3D::Playback3D(MWindow *mwindow)
386 this->mwindow = mwindow;
389 //Enabling OpenGL debug output
390 // this does not work
391 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
392 glEnable(GL_DEBUG_OUTPUT);
393 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
394 glDebugMessageCallback(glDebugCallback, 0);
395 glEnable(GL_DEBUG_OUTPUT);
399 Playback3D::~Playback3D()
406 BC_SynchronousCommand* Playback3D::new_command()
408 return new Playback3DCommand;
413 void Playback3D::handle_command(BC_SynchronousCommand *command)
415 //printf("Playback3D::handle_command 1 %d\n", command->command);
416 switch(command->command)
418 case Playback3DCommand::WRITE_BUFFER:
419 write_buffer_sync((Playback3DCommand*)command);
422 case Playback3DCommand::FINISH_OUTPUT:
423 finish_output_sync((Playback3DCommand*)command);
426 case Playback3DCommand::CLEAR_OUTPUT:
427 clear_output_sync((Playback3DCommand*)command);
430 case Playback3DCommand::CLEAR_INPUT:
431 clear_input_sync((Playback3DCommand*)command);
434 case Playback3DCommand::DO_CAMERA:
435 do_camera_sync((Playback3DCommand*)command);
438 case Playback3DCommand::OVERLAY:
439 overlay_sync((Playback3DCommand*)command);
442 case Playback3DCommand::DO_FADE:
443 do_fade_sync((Playback3DCommand*)command);
446 case Playback3DCommand::DO_MASK:
447 do_mask_sync((Playback3DCommand*)command);
450 case Playback3DCommand::PLUGIN:
451 run_plugin_sync((Playback3DCommand*)command);
454 case Playback3DCommand::COPY_FROM:
455 copy_from_sync((Playback3DCommand*)command);
458 case Playback3DCommand::CONVERT_CMODEL:
459 convert_cmodel_sync((Playback3DCommand*)command);
462 // case Playback3DCommand::DRAW_REFRESH:
463 // draw_refresh_sync((Playback3DCommand*)command);
466 //printf("Playback3D::handle_command 10\n");
472 void Playback3D::copy_from(Canvas *canvas,
477 Playback3DCommand command;
478 command.command = Playback3DCommand::COPY_FROM;
479 command.canvas = canvas;
482 command.want_texture = want_texture;
483 send_command(&command);
486 void Playback3D::copy_from_sync(Playback3DCommand *command)
489 BC_WindowBase *window =
490 command->canvas->lock_canvas("Playback3D::copy_from_sync");
492 window->enable_opengl();
493 int w = command->input->get_w();
494 int h = command->input->get_h();
496 if(command->input->get_opengl_state() == VFrame::SCREEN &&
497 w == command->frame->get_w() && h == command->frame->get_h())
499 // printf("Playback3D::copy_from_sync 1 %d %d %d %d %d\n",
500 // command->input->get_w(),
501 // command->input->get_h(),
502 // command->frame->get_w(),
503 // command->frame->get_h(),
504 // command->frame->get_color_model());
505 // With NVidia at least,
508 printf("Playback3D::copy_from_sync: w=%d not supported because it is not divisible by 4.\n", w);
512 if(command->want_texture)
514 //printf("Playback3D::copy_from_sync 1 dst=%p src=%p\n", command->frame, command->input);
515 // Screen_to_texture requires the source pbuffer enabled.
516 command->input->enable_opengl();
517 command->frame->screen_to_texture();
518 command->frame->set_opengl_state(VFrame::TEXTURE);
523 command->input->to_texture();
524 command->input->bind_texture(0);
525 command->frame->enable_opengl();
526 command->frame->init_screen();
527 unsigned int shader = BC_CModels::is_yuv(command->input->get_color_model()) ?
528 VFrame::make_shader(0, yuv_to_rgb_frag, 0) : 0;
530 glUseProgram(shader);
531 int variable = glGetUniformLocation(shader, "tex");
532 glUniform1i(variable, 0);
533 BC_GL_YUV_TO_RGB(shader);
537 command->input->draw_texture(1);
538 command->frame->screen_to_ram();
544 printf("Playback3D::copy_from_sync: invalid formats opengl_state=%d %dx%d -> %dx%d\n",
545 command->input->get_opengl_state(), w, h,
546 command->frame->get_w(), command->frame->get_h());
549 command->canvas->unlock_canvas();
556 // void Playback3D::draw_refresh(Canvas *canvas,
567 // Playback3DCommand command;
568 // command.command = Playback3DCommand::DRAW_REFRESH;
569 // command.canvas = canvas;
570 // command.frame = frame;
571 // command.in_x1 = in_x1;
572 // command.in_y1 = in_y1;
573 // command.in_x2 = in_x2;
574 // command.in_y2 = in_y2;
575 // command.out_x1 = out_x1;
576 // command.out_y1 = out_y1;
577 // command.out_x2 = out_x2;
578 // command.out_y2 = out_y2;
579 // send_command(&command);
582 // void Playback3D::draw_refresh_sync(Playback3DCommand *command)
585 // BC_WindowBase *window =
586 // command->canvas->lock_canvas("Playback3D::draw_refresh_sync");
588 // window->enable_opengl();
590 // // Read output pbuffer back to RAM in project colormodel
591 // // RGB 8bit is fastest for OpenGL to read back.
592 // command->frame->reallocate(0,
596 // command->frame->get_w(),
597 // command->frame->get_h(),
600 // command->frame->screen_to_ram();
602 // window->clear_box(0,
606 // window->draw_vframe(command->frame,
607 // (int)command->out_x1,
608 // (int)command->out_y1,
609 // (int)(command->out_x2 - command->out_x1),
610 // (int)(command->out_y2 - command->out_y1),
611 // (int)command->in_x1,
612 // (int)command->in_y1,
613 // (int)(command->in_x2 - command->in_x1),
614 // (int)(command->in_y2 - command->in_y1),
618 // command->canvas->unlock_canvas();
626 void Playback3D::write_buffer(Canvas *canvas,
638 Playback3DCommand command;
639 command.command = Playback3DCommand::WRITE_BUFFER;
640 command.canvas = canvas;
641 command.frame = frame;
642 command.in_x1 = in_x1;
643 command.in_y1 = in_y1;
644 command.in_x2 = in_x2;
645 command.in_y2 = in_y2;
646 command.out_x1 = out_x1;
647 command.out_y1 = out_y1;
648 command.out_x2 = out_x2;
649 command.out_y2 = out_y2;
650 command.is_cleared = is_cleared;
651 send_command(&command);
655 void Playback3D::write_buffer_sync(Playback3DCommand *command)
658 BC_WindowBase *window =
659 command->canvas->lock_canvas("Playback3D::write_buffer_sync");
661 // Update hidden cursor
662 window->update_video_cursor();
663 // Make sure OpenGL is enabled first.
664 window->enable_opengl();
666 //printf("Playback3D::write_buffer_sync 1 %d\n", window->get_id());
667 int flip_y = 0, frame_state = command->frame->get_opengl_state();
668 switch( frame_state ) {
669 // Upload texture and composite to screen
673 command->frame->to_texture();
674 window->enable_opengl();
675 // Composite texture to screen and swap buffer
676 case VFrame::TEXTURE:
678 int fh1 = command->frame->get_h()-1;
679 float in_y1 = fh1 - command->in_y1;
680 float in_y2 = fh1 - command->in_y2;
681 command->in_y1 = in_y2;
682 command->in_y2 = in_y1;
684 draw_output(command, flip_y);
687 printf("Playback3D::write_buffer_sync unknown state\n");
690 command->frame->set_opengl_state(frame_state);
692 command->canvas->unlock_canvas();
698 void Playback3D::draw_output(Playback3DCommand *command, int flip_y)
701 int texture_id = command->frame->get_texture_id();
702 BC_WindowBase *window = command->canvas->get_canvas();
704 // printf("Playback3D::draw_output 1 texture_id=%d window=%p\n",
706 // command->canvas->get_canvas());
711 // If virtual console is being used, everything in this function has
712 // already been done except the page flip.
715 canvas_w = window->get_w();
716 canvas_h = window->get_h();
717 VFrame::init_screen(canvas_w, canvas_h);
718 int color_model = command->frame->get_color_model();
719 int is_yuv = BC_CModels::is_yuv(color_model);
721 if(!command->is_cleared)
723 // If we get here, the virtual console was not used.
724 init_frame(command, 0);
728 // Undo any previous shader settings
729 command->frame->bind_texture(0);
731 // Convert colormodel
732 unsigned int shader = is_yuv ? VFrame::make_shader(0, yuv_to_rgb_frag, 0) : 0;
734 glUseProgram(shader);
735 // Set texture unit of the texture
736 int variable = glGetUniformLocation(shader, "tex");
737 glUniform1i(variable, 0);
738 BC_GL_YUV_TO_RGB(shader);
741 // if(BC_CModels::components(color_model) == 4)
743 // glEnable(GL_BLEND);
744 // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
747 command->frame->draw_texture(
748 command->in_x1, command->in_y1, command->in_x2, command->in_y2,
749 command->out_x1, command->out_y1, command->out_x2, command->out_y2,
753 //printf("Playback3D::draw_output 2 %f,%f %f,%f -> %f,%f %f,%f\n",
765 command->canvas->get_canvas()->flip_opengl();
772 void Playback3D::init_frame(Playback3DCommand *command, int is_yuv)
775 float gbuv = is_yuv ? 0.5 : 0.0;
776 glClearColor(0.0, gbuv, gbuv, 0.0);
777 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
782 void Playback3D::finish_output(Canvas *canvas)
784 Playback3DCommand command;
785 command.canvas = canvas;
786 command.command = Playback3DCommand::FINISH_OUTPUT;
787 send_command(&command);
790 void Playback3D::finish_output_sync(Playback3DCommand *command)
793 BC_WindowBase *window =
794 command->canvas->lock_canvas("Playback3D::finish_output_sync");
796 command->canvas->get_canvas()->enable_opengl();
799 command->canvas->unlock_canvas();
804 void Playback3D::clear_output(Canvas *canvas, VFrame *output)
806 Playback3DCommand command;
807 command.command = Playback3DCommand::CLEAR_OUTPUT;
808 command.canvas = canvas;
809 command.frame = output;
810 send_command(&command);
813 void Playback3D::clear_output_sync(Playback3DCommand *command)
816 BC_WindowBase *window =
817 command->canvas->lock_canvas("Playback3D::clear_output_sync");
819 // If we get here, the virtual console is being used.
820 command->canvas->get_canvas()->enable_opengl();
822 // Using pbuffer for refresh frame.
823 if( command->frame ) {
824 command->frame->enable_opengl();
825 int color_model = command->canvas->mwindow->edl->session->color_model;
826 is_yuv = BC_CModels::is_yuv(color_model);
829 init_frame(command, is_yuv);
831 command->canvas->unlock_canvas();
836 void Playback3D::clear_input(Canvas *canvas, VFrame *frame)
838 Playback3DCommand command;
839 command.command = Playback3DCommand::CLEAR_INPUT;
840 command.canvas = canvas;
841 command.frame = frame;
842 send_command(&command);
845 void Playback3D::clear_input_sync(Playback3DCommand *command)
848 BC_WindowBase *window =
849 command->canvas->lock_canvas("Playback3D::clear_input_sync");
851 command->canvas->get_canvas()->enable_opengl();
852 command->frame->enable_opengl();
853 command->frame->clear_pbuffer();
854 command->frame->set_opengl_state(VFrame::SCREEN);
856 command->canvas->unlock_canvas();
860 void Playback3D::do_camera(Canvas *canvas,
872 Playback3DCommand command;
873 command.command = Playback3DCommand::DO_CAMERA;
874 command.canvas = canvas;
875 command.input = input;
876 command.frame = output;
877 command.in_x1 = in_x1;
878 command.in_y1 = in_y1;
879 command.in_x2 = in_x2;
880 command.in_y2 = in_y2;
881 command.out_x1 = out_x1;
882 command.out_y1 = out_y1;
883 command.out_x2 = out_x2;
884 command.out_y2 = out_y2;
885 send_command(&command);
888 void Playback3D::do_camera_sync(Playback3DCommand *command)
891 BC_WindowBase *window =
892 command->canvas->lock_canvas("Playback3D::do_camera_sync");
894 command->canvas->get_canvas()->enable_opengl();
896 command->input->to_texture();
897 command->frame->enable_opengl();
898 command->frame->init_screen();
899 command->frame->clear_pbuffer();
901 command->input->bind_texture(0);
902 // Must call draw_texture in input frame to get the texture coordinates right.
904 // printf("Playback3D::do_camera_sync 1 %.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f\n",
910 // (float)command->input->get_h() - command->out_y1,
912 // (float)command->input->get_h() - command->out_y2);
913 command->input->draw_texture(
914 command->in_x1, command->in_y2,
915 command->in_x2, command->in_y1,
917 (float)command->frame->get_h() - command->out_y1,
919 (float)command->frame->get_h() - command->out_y2);
922 command->frame->set_opengl_state(VFrame::SCREEN);
923 command->frame->screen_to_ram();
925 command->canvas->unlock_canvas();
929 void Playback3D::overlay(Canvas *canvas, VFrame *input,
930 float in_x1, float in_y1, float in_x2, float in_y2,
931 float out_x1, float out_y1, float out_x2, float out_y2,
932 float alpha, int mode, int interpolation_type,
933 VFrame *output, int is_nested)
935 Playback3DCommand command;
936 command.command = Playback3DCommand::OVERLAY;
937 command.canvas = canvas;
938 command.frame = output;
939 command.input = input;
940 command.in_x1 = in_x1;
941 command.in_y1 = in_y1;
942 command.in_x2 = in_x2;
943 command.in_y2 = in_y2;
944 command.out_x1 = out_x1;
945 command.out_y1 = out_y1;
946 command.out_x2 = out_x2;
947 command.out_y2 = out_y2;
948 command.alpha = alpha;
950 command.interpolation_type = interpolation_type;
951 command.is_nested = is_nested;
952 send_command(&command);
955 void Playback3D::overlay_sync(Playback3DCommand *command)
958 // To do these operations, we need to copy the input buffer to a texture
959 // and blend 2 textures in a shader
960 static const char * const overlay_shaders[TRANSFER_TYPES] = {
961 blend_NORMAL_frag, // TRANSFER_NORMAL
962 blend_ADDITION_frag, // TRANSFER_ADDITION
963 blend_SUBTRACT_frag, // TRANSFER_SUBTRACT
964 blend_MULTIPLY_frag, // TRANSFER_MULTIPLY
965 blend_DIVIDE_frag, // TRANSFER_DIVIDE
966 blend_REPLACE_frag, // TRANSFER_REPLACE
967 blend_MAX_frag, // TRANSFER_MAX
968 blend_MIN_frag, // TRANSFER_MIN
969 blend_DARKEN_frag, // TRANSFER_DARKEN
970 blend_LIGHTEN_frag, // TRANSFER_LIGHTEN
971 blend_DST_frag, // TRANSFER_DST
972 blend_DST_ATOP_frag, // TRANSFER_DST_ATOP
973 blend_DST_IN_frag, // TRANSFER_DST_IN
974 blend_DST_OUT_frag, // TRANSFER_DST_OUT
975 blend_DST_OVER_frag, // TRANSFER_DST_OVER
976 blend_SRC_frag, // TRANSFER_SRC
977 blend_SRC_ATOP_frag, // TRANSFER_SRC_ATOP
978 blend_SRC_IN_frag, // TRANSFER_SRC_IN
979 blend_SRC_OUT_frag, // TRANSFER_SRC_OUT
980 blend_SRC_OVER_frag, // TRANSFER_SRC_OVER
981 blend_AND_frag, // TRANSFER_AND
982 blend_OR_frag, // TRANSFER_OR
983 blend_XOR_frag, // TRANSFER_XOR
984 blend_OVERLAY_frag, // TRANSFER_OVERLAY
985 blend_SCREEN_frag, // TRANSFER_SCREEN
986 blend_BURN_frag, // TRANSFER_BURN
987 blend_DODGE_frag, // TRANSFER_DODGE
988 blend_HARDLIGHT_frag, // TRANSFER_HARDLIGHT
989 blend_SOFTLIGHT_frag, // TRANSFER_SOFTLIGHT
990 blend_DIFFERENCE_frag, // TRANSFER_DIFFERENCE
993 BC_WindowBase *window =
994 command->canvas->lock_canvas("Playback3D::overlay_sync");
996 // Make sure OpenGL is enabled first.
997 window->enable_opengl();
998 window->update_video_cursor();
1000 glColor4f(1, 1, 1, 1);
1001 glDisable(GL_BLEND);
1004 //printf("Playback3D::overlay_sync 1 %d\n", command->input->get_opengl_state());
1005 switch( command->input->get_opengl_state() ) {
1006 // Upload texture and composite to screen
1008 command->input->to_texture();
1010 // Just composite texture to screen
1011 case VFrame::TEXTURE:
1013 // read from PBuffer to texture, then composite texture to screen
1014 case VFrame::SCREEN:
1015 command->input->enable_opengl();
1016 command->input->screen_to_texture();
1019 printf("Playback3D::overlay_sync unknown state\n");
1023 if(command->frame) {
1024 // Render to PBuffer
1025 command->frame->enable_opengl();
1026 command->frame->set_opengl_state(VFrame::SCREEN);
1027 canvas_w = command->frame->get_w();
1028 canvas_h = command->frame->get_h();
1032 window->enable_opengl();
1033 canvas_w = window->get_w();
1034 canvas_h = window->get_h();
1038 const char *shader_stack[16];
1039 memset(shader_stack,0, sizeof(shader_stack));
1040 int total_shaders = 0, need_matrix = 0;
1042 VFrame::init_screen(canvas_w, canvas_h);
1045 command->input->bind_texture(0);
1047 // Convert colormodel to RGB if not nested.
1048 // The color model setting in the output frame is ignored.
1049 // if( command->is_nested <= 0 && // not nested
1050 // BC_CModels::is_yuv(command->input->get_color_model()) ) {
1052 // shader_stack[total_shaders++] = yuv_to_rgb_frag;
1056 #define add_shader(s) \
1057 if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag; \
1058 shader_stack[total_shaders++] = s
1060 switch(command->mode) {
1061 case TRANSFER_REPLACE:
1062 // This requires overlaying an alpha multiplied image on a black screen.
1063 if( command->input->get_texture_components() != 4 ) break;
1064 add_shader(overlay_shaders[command->mode]);
1067 enable_overlay_texture(command);
1068 add_shader(overlay_shaders[command->mode]);
1072 // if to flatten alpha
1073 // if( command->is_nested < 0 ) {
1074 // switch(command->input->get_color_model()) {
1075 //// yuv has already been converted to rgb
1076 // case BC_YUVA8888:
1077 // case BC_RGBA_FLOAT:
1078 // case BC_RGBA8888:
1079 // add_shader(rgba_to_rgb_flatten);
1086 unsigned int shader = !shader_stack[0] ? 0 :
1087 VFrame::make_shader(shader_stack);
1089 glUseProgram(shader);
1090 if( need_matrix ) BC_GL_YUV_TO_RGB(shader);
1091 // Set texture unit of the texture
1092 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1093 // Set texture unit of the temp texture
1094 glUniform1i(glGetUniformLocation(shader, "tex2"), 1);
1096 int variable = glGetUniformLocation(shader, "alpha");
1097 glUniform1f(variable, command->alpha);
1098 // Set dimensions of the temp texture
1100 glUniform2f(glGetUniformLocation(shader, "tex2_dimensions"),
1101 (float)temp_texture->get_texture_w(),
1102 (float)temp_texture->get_texture_h());
1108 //printf("Playback3D::overlay_sync %f %f %f %f %f %f %f %f\n",
1109 // command->in_x1, command->in_y1, command->in_x2, command->in_y2,
1110 // command->out_x1, command->out_y1, command->out_x2, command->out_y2);
1112 command->input->draw_texture(
1113 command->in_x1, command->in_y1, command->in_x2, command->in_y2,
1114 command->out_x1, command->out_y1, command->out_x2, command->out_y2,
1115 !command->is_nested);
1118 // Delete temp texture
1120 delete temp_texture;
1122 glActiveTexture(GL_TEXTURE1);
1123 glDisable(GL_TEXTURE_2D);
1125 glActiveTexture(GL_TEXTURE0);
1126 glDisable(GL_TEXTURE_2D);
1128 command->canvas->unlock_canvas();
1132 void Playback3D::enable_overlay_texture(Playback3DCommand *command)
1135 glDisable(GL_BLEND);
1137 glActiveTexture(GL_TEXTURE1);
1138 BC_Texture::new_texture(&temp_texture, canvas_w, canvas_h,
1139 command->input->get_color_model());
1140 temp_texture->bind(1);
1142 // Read canvas into texture
1143 glReadBuffer(GL_BACK);
1144 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, canvas_w, canvas_h);
1149 void Playback3D::do_mask(Canvas *canvas,
1151 int64_t start_position_project,
1152 MaskAutos *keyframe_set,
1154 MaskAuto *default_auto)
1156 Playback3DCommand command;
1157 command.command = Playback3DCommand::DO_MASK;
1158 command.canvas = canvas;
1159 command.frame = output;
1160 command.start_position_project = start_position_project;
1161 command.keyframe_set = keyframe_set;
1162 command.keyframe = keyframe;
1163 command.default_auto = default_auto;
1165 send_command(&command);
1169 void Playback3D::draw_spots(MaskSpots &spots, int ix1,int iy1, int ix2,int iy2)
1171 int x1 = iy1 < iy2 ? ix1 : ix2;
1172 int y1 = iy1 < iy2 ? iy1 : iy2;
1173 int x2 = iy1 < iy2 ? ix2 : ix1;
1174 int y2 = iy1 < iy2 ? iy2 : iy1;
1177 int dx = x2-x1, dy = y2-y1;
1178 int dx2 = 2*dx, dy2 = 2*dy;
1179 if( dx < 0 ) dx = -dx;
1180 int m = dx > dy ? dx : dy, n = m;
1182 if( dx2 >= 0 ) do { /* +Y, +X */
1183 spots.append(x, y++);
1184 if( (m -= dx2) < 0 ) { m += dy2; ++x; }
1185 } while( --n >= 0 );
1186 else do { /* +Y, -X */
1187 spots.append(x, y++);
1188 if( (m += dx2) < 0 ) { m += dy2; --x; }
1189 } while( --n >= 0 );
1192 if( dx2 >= 0 ) do { /* +X, +Y */
1193 spots.append(x++, y);
1194 if( (m -= dy2) < 0 ) { m += dx2; ++y; }
1195 } while( --n >= 0 );
1196 else do { /* -X, +Y */
1197 spots.append(x--, y);
1198 if( (m -= dy2) < 0 ) { m -= dx2; ++y; }
1199 } while( --n >= 0 );
1204 class fb_texture : public BC_Texture
1207 fb_texture(int w, int h, int colormodel);
1209 void bind(int texture_unit);
1210 void read_screen(int x, int y, int w, int h);
1211 void set_output_texture();
1215 fb_texture::fb_texture(int w, int h, int colormodel)
1216 : BC_Texture(w, h, colormodel)
1219 // glGenRenderbuffers(1, &rb);
1220 // glBindRenderbuffer(GL_RENDERBUFFER, rb);
1221 // glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, get_texture_w(), get_texture_w());
1222 glGenFramebuffers(1, &fb);
1223 glBindFramebuffer(GL_FRAMEBUFFER, fb);
1224 // glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rb);
1227 fb_texture::~fb_texture()
1229 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1230 glDeleteFramebuffers(1, (GLuint *)&fb);
1231 // glBindRenderbuffer(GL_RENDERBUFFER, 0);
1232 // glGenRenderbuffers(1, &rb);
1235 void fb_texture::bind(int texture_unit)
1237 glBindFramebuffer(GL_FRAMEBUFFER, fb);
1238 // glBindRenderbuffer(GL_RENDERBUFFER, rb);
1239 BC_Texture::bind(texture_unit);
1242 void fb_texture::read_screen(int x, int y, int w, int h)
1244 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1245 glReadBuffer(GL_BACK);
1246 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, x,y, w,h);
1249 void fb_texture::set_output_texture()
1251 glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, get_texture_id(), 0);
1252 GLenum dbo[1] = { GL_COLOR_ATTACHMENT0, }; // bind layout(location=0) out vec4 color;
1253 glDrawBuffers(1, dbo);
1254 int ret = glCheckFramebufferStatus(GL_FRAMEBUFFER);
1255 if( ret != GL_FRAMEBUFFER_COMPLETE ) {
1256 printf("glDrawBuffer error 0x%04x\n", ret);
1261 static void combineData(GLdouble coords[3],
1262 GLdouble *vertex_data[4], GLfloat weight[4],
1263 GLdouble **outData, void *data)
1265 ArrayList<double *> *invented = (ArrayList<double *> *)data;
1266 GLdouble *vertex = new double[6];
1267 invented->append(vertex);
1268 vertex[0] = coords[0];
1269 vertex[1] = coords[1];
1270 vertex[2] = coords[2];
1271 for( int i=3; i<6; ++i ) {
1272 vertex[i] = weight[0] * vertex_data[0][i] +
1273 weight[1] * vertex_data[1][i] +
1274 weight[2] * vertex_data[2][i] +
1275 weight[3] * vertex_data[3][i];
1281 static void zglBegin(GLenum mode) { glBegin(mode); }
1282 static void zglEnd() { glEnd(); }
1283 static void zglVertex3dv(const GLdouble *v) { glVertex3dv(v); }
1287 void Playback3D::do_mask_sync(Playback3DCommand *command)
1290 BC_WindowBase *window =
1291 command->canvas->lock_canvas("Playback3D::do_mask_sync");
1293 window->enable_opengl();
1295 switch( command->frame->get_opengl_state() ) {
1297 // upload frame to the texture
1298 command->frame->to_texture();
1301 case VFrame::SCREEN:
1302 // Read back from PBuffer
1303 // Bind context to pbuffer
1304 command->frame->enable_opengl();
1305 command->frame->screen_to_texture();
1309 // Initialize coordinate system
1310 command->frame->enable_opengl();
1311 command->frame->init_screen();
1312 int color_model = command->frame->get_color_model();
1313 int w = command->frame->get_w();
1314 int h = command->frame->get_h();
1316 float faders[SUBMASKS], feathers[SUBMASKS], bg = 1;
1317 MaskPointSet point_set[SUBMASKS];
1318 // Draw every submask as a new polygon
1319 int total_submasks = command->keyframe_set->total_submasks(
1320 command->start_position_project, PLAY_FORWARD);
1322 for(int k = 0; k < total_submasks; k++) {
1323 MaskPointSet &points = point_set[k];
1324 command->keyframe_set->get_points(&points,
1325 k, command->start_position_project, PLAY_FORWARD);
1326 float fader = command->keyframe_set->get_fader(
1327 command->start_position_project, k, PLAY_FORWARD);
1328 float v = fader/100.;
1330 if( v < 0 && (v+=1) < bg ) bg = v;
1331 float feather = command->keyframe_set->get_feather(
1332 command->start_position_project, k, PLAY_FORWARD);
1333 feathers[k] = feather;
1336 glDisable(GL_TEXTURE_2D);
1337 glClearColor(bg, bg, bg, bg);
1338 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1340 for(int k = 0; k < total_submasks; k++) {
1341 MaskPointSet &points = point_set[k];
1342 MaskEdge &edge = *edges.append(new MaskEdge());
1343 int first_point = 0;
1344 // Need to tabulate every vertex in persistent memory because
1345 // gluTessVertex doesn't copy them.
1346 for(int i = 0; i < points.total; i++) {
1347 MaskPoint *point1 = points.values[i];
1348 MaskPoint *point2 = (i >= points.total - 1) ?
1349 points.values[0] : points.values[i + 1];
1353 if( point1->control_x2 == 0 && point1->control_y2 == 0 &&
1354 point2->control_x1 == 0 && point2->control_y1 == 0 )
1357 float x0 = point1->x, y0 = point1->y;
1358 float x1 = point1->x + point1->control_x2;
1359 float y1 = point1->y + point1->control_y2;
1360 float x2 = point2->x + point2->control_x1;
1361 float y2 = point2->y + point2->control_y1;
1362 float x3 = point2->x, y3 = point2->y;
1364 // forward differencing bezier curves implementation taken from GPL code at
1365 // http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3
1367 float cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
1374 cx3 = - x0 + 3*x1 - 3*x2 + x3;
1375 cx2 = 3*x0 - 6*x1 + 3*x2;
1379 cy3 = - y0 + 3*y1 - 3*y2 + y3;
1380 cy2 = 3*y0 - 6*y1 + 3*y2;
1384 // This equation is from Graphics Gems I.
1386 // The idea is that since we're approximating a cubic curve with lines,
1387 // any error we incur is due to the curvature of the line, which we can
1388 // estimate by calculating the maximum acceleration of the curve. For
1389 // a cubic, the acceleration (second derivative) is a line, meaning that
1390 // the absolute maximum acceleration must occur at either the beginning
1391 // (|c2|) or the end (|c2+c3|). Our bounds here are a little more
1392 // conservative than that, but that's okay.
1393 if (segments == 0) {
1394 float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
1395 float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
1397 float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
1400 if(maxaccel > 8.0) h = sqrt((8.0) / maxaccel);
1401 segments = int(1/h);
1404 for(int j = 0; j <= segments; j++) {
1405 float t = (float)j / segments;
1406 x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
1407 y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
1409 if(j > 0 || first_point) {
1410 edge.append(x, y - h);
1415 if( edge.size() > 0 ) {
1417 float fader = faders[k];
1418 float v = fader < 0 ? 1 : 1-fader;
1419 glColor4f(v, v, v, v);
1420 int display_list = glGenLists(1);
1421 glNewList(display_list, GL_COMPILE);
1423 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
1424 glBegin(GL_POLYGON);
1425 MaskCoord *c = &edge[0];
1426 for( int i=edge.size(); --i>=0; ++c )
1427 glVertex2f(c->x, c->y);
1430 GLUtesselator *tess = gluNewTess();
1431 gluTessCallback(tess, GLU_TESS_VERTEX,(GLvoid (*)()) &zglVertex3dv);
1432 gluTessCallback(tess, GLU_TESS_BEGIN,(GLvoid (*)()) &zglBegin);
1433 gluTessCallback(tess, GLU_TESS_END,(GLvoid (*)()) &zglEnd);
1434 gluTessCallback(tess, GLU_TESS_COMBINE_DATA,(GLvoid (*)()) &combineData);
1435 ArrayList<double *> invented;
1436 invented.set_array_delete();
1438 gluTessBeginPolygon(tess, &invented);
1439 gluTessBeginContour(tess);
1440 MaskCoord *c = &edge[0];
1441 for( int i=edge.size(); --i>=0; ++c )
1442 gluTessVertex(tess, (GLdouble *)c, c);
1443 gluTessEndContour(tess);
1444 gluTessEndPolygon(tess);
1445 gluDeleteTess(tess);
1446 invented.remove_all_objects();
1449 glCallList(display_list);
1450 glDeleteLists(1, display_list);
1455 fb_texture *in = new fb_texture(w, h, color_model);
1457 in->read_screen(0,0, w,h);
1458 fb_texture *out = new fb_texture(w, h, color_model);
1460 unsigned int frag_shader =
1461 VFrame::make_shader(0, in_vertex_frag, feather_frag, 0);
1462 if( frag_shader > 0 ) {
1464 glGenBuffers(1, points);
1465 for(int k = 0; k < total_submasks; k++) {
1466 MaskEdge &edge = *edges[k];
1467 if( !edge.size() ) continue;
1468 if( !faders[k] ) continue;
1469 if( !feathers[k] ) continue;
1471 for( int i=0; i<edge.size(); ++i ) {
1472 MaskCoord &a = edge[i];
1473 MaskCoord &b = i<edge.size()-1 ? edge[i+1] : edge[0];
1474 draw_spots(spots, a.x,a.y+h, b.x,b.y+h);
1476 int sz = spots.size() * sizeof(MaskSpot);
1477 glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, points[0], 0, sz);
1478 glBufferData(GL_SHADER_STORAGE_BUFFER, sz, &spots[0], GL_DYNAMIC_COPY);
1479 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
1480 glUseProgram(frag_shader);
1481 float r = feathers[k], v = faders[k];
1482 glUniform1f(glGetUniformLocation(frag_shader, "r"), r);
1483 glUniform1f(glGetUniformLocation(frag_shader, "v"), v);
1485 glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
1486 out->set_output_texture();
1487 glViewport(0,0, w,h);
1488 out->draw_texture(0,0, w,h, 0,0, w,h);
1490 fb_texture *t = in; in = out; out = t;
1492 glDeleteBuffers(1, points);
1495 glDrawBuffers(0, 0);
1496 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1498 unsigned int shader = VFrame::make_shader(0, alpha_frag, 0);
1499 glUseProgram(shader);
1501 command->frame->bind_texture(0);
1502 in->BC_Texture::bind(1);
1503 glUniform1i(glGetUniformLocation(shader, "tex"), 0);
1504 glUniform1i(glGetUniformLocation(shader, "tex2"), 1);
1505 glUniform2f(glGetUniformLocation(shader, "tex2_dimensions"),
1506 (float)in->get_texture_w(),
1507 (float)in->get_texture_h());
1508 // if( BC_CModels::components(color_model ) == 4) {
1509 // glEnable(GL_BLEND);
1510 // glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1513 command->frame->draw_texture();
1514 command->frame->set_opengl_state(VFrame::SCREEN);
1519 glDisable(GL_TEXTURE_2D);
1520 glColor4f(1, 1, 1, 1);
1521 glActiveTexture(GL_TEXTURE0);
1522 window->enable_opengl();
1524 command->canvas->unlock_canvas();
1529 void Playback3D::convert_cmodel(Canvas *canvas,
1533 // Do nothing if colormodels are equivalent in OpenGL & the image is in hardware.
1534 int src_cmodel = output->get_color_model();
1536 (output->get_opengl_state() == VFrame::TEXTURE ||
1537 output->get_opengl_state() == VFrame::SCREEN) &&
1538 // OpenGL has no floating point.
1539 ( (src_cmodel == BC_RGB888 && dst_cmodel == BC_RGB_FLOAT) ||
1540 (src_cmodel == BC_RGBA8888 && dst_cmodel == BC_RGBA_FLOAT) ||
1541 (src_cmodel == BC_RGB_FLOAT && dst_cmodel == BC_RGB888) ||
1542 (src_cmodel == BC_RGBA_FLOAT && dst_cmodel == BC_RGBA8888) ||
1543 // OpenGL sets alpha to 1 on import
1544 (src_cmodel == BC_RGB888 && dst_cmodel == BC_RGBA8888) ||
1545 (src_cmodel == BC_YUV888 && dst_cmodel == BC_YUVA8888) ||
1546 (src_cmodel == BC_RGB_FLOAT && dst_cmodel == BC_RGBA_FLOAT) )
1551 Playback3DCommand command;
1552 command.command = Playback3DCommand::CONVERT_CMODEL;
1553 command.canvas = canvas;
1554 command.frame = output;
1555 command.dst_cmodel = dst_cmodel;
1556 send_command(&command);
1559 void Playback3D::convert_cmodel_sync(Playback3DCommand *command)
1562 BC_WindowBase *window =
1563 command->canvas->lock_canvas("Playback3D::convert_cmodel_sync");
1565 window->enable_opengl();
1567 // Import into hardware
1568 command->frame->enable_opengl();
1569 command->frame->init_screen();
1570 command->frame->to_texture();
1572 // Colormodel permutation
1573 int src_cmodel = command->frame->get_color_model();
1574 int dst_cmodel = command->dst_cmodel;
1578 } cmodel_shader_table_t;
1579 enum { rgb_to_rgb, rgb_to_yuv, yuv_to_rgb, yuv_to_yuv, };
1581 static cmodel_shader_table_t cmodel_shader_table[] = {
1582 { BC_RGB888, BC_YUV888, rgb_to_yuv, rgb_to_yuv_frag },
1583 { BC_RGB888, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag },
1584 { BC_RGBA8888, BC_RGB888, rgb_to_rgb, rgba_to_rgb_frag },
1585 { BC_RGBA8888, BC_RGB_FLOAT, rgb_to_rgb, rgba_to_rgb_frag },
1586 { BC_RGBA8888, BC_YUV888, rgb_to_yuv, rgba_to_yuv_frag },
1587 { BC_RGBA8888, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag },
1588 { BC_RGB_FLOAT, BC_YUV888, rgb_to_yuv, rgb_to_yuv_frag },
1589 { BC_RGB_FLOAT, BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag },
1590 { BC_RGBA_FLOAT,BC_RGB888, rgb_to_rgb, rgba_to_rgb_frag },
1591 { BC_RGBA_FLOAT,BC_RGB_FLOAT, rgb_to_rgb, rgba_to_rgb_frag },
1592 { BC_RGBA_FLOAT,BC_YUV888, rgb_to_yuv, rgba_to_yuv_frag },
1593 { BC_RGBA_FLOAT,BC_YUVA8888, rgb_to_yuv, rgb_to_yuv_frag },
1594 { BC_YUV888, BC_RGB888, yuv_to_rgb, yuv_to_rgb_frag },
1595 { BC_YUV888, BC_RGBA8888, yuv_to_rgb, yuv_to_rgb_frag },
1596 { BC_YUV888, BC_RGB_FLOAT, yuv_to_rgb, yuv_to_rgb_frag },
1597 { BC_YUV888, BC_RGBA_FLOAT, yuv_to_rgb, yuv_to_rgb_frag },
1598 { BC_YUVA8888, BC_RGB888, yuv_to_rgb, yuva_to_rgb_frag },
1599 { BC_YUVA8888, BC_RGBA8888, yuv_to_rgb, yuv_to_rgb_frag },
1600 { BC_YUVA8888, BC_RGB_FLOAT, yuv_to_rgb, yuva_to_rgb_frag },
1601 { BC_YUVA8888, BC_RGBA_FLOAT, yuv_to_rgb, yuv_to_rgb_frag },
1602 { BC_YUVA8888, BC_YUV888, yuv_to_yuv, yuva_to_yuv_frag },
1605 const char *shader = 0;
1606 int table_size = sizeof(cmodel_shader_table) / sizeof(cmodel_shader_table_t);
1607 for( int i=0; i<table_size; ++i ) {
1608 if( cmodel_shader_table[i].src == src_cmodel &&
1609 cmodel_shader_table[i].dst == dst_cmodel ) {
1610 shader = cmodel_shader_table[i].shader;
1611 type = cmodel_shader_table[i].typ;
1616 // printf("Playback3D::convert_cmodel_sync %d %d %d shader=\n%s",
1618 // command->frame->get_color_model(),
1619 // command->dst_cmodel,
1622 const char *shader_stack[9];
1623 memset(shader_stack,0, sizeof(shader_stack));
1624 int current_shader = 0;
1627 //printf("Playback3D::convert_cmodel_sync %d\n", __LINE__);
1628 shader_stack[current_shader++] = shader;
1629 shader_stack[current_shader] = 0;
1630 unsigned int shader_id = VFrame::make_shader(shader_stack);
1632 command->frame->bind_texture(0);
1633 glUseProgram(shader_id);
1635 glUniform1i(glGetUniformLocation(shader_id, "tex"), 0);
1638 BC_GL_RGB_TO_YUV(shader_id);
1641 BC_GL_YUV_TO_RGB(shader_id);
1645 command->frame->draw_texture();
1646 if(shader) glUseProgram(0);
1647 command->frame->set_opengl_state(VFrame::SCREEN);
1651 command->canvas->unlock_canvas();
1655 void Playback3D::do_fade(Canvas *canvas, VFrame *frame, float fade)
1657 Playback3DCommand command;
1658 command.command = Playback3DCommand::DO_FADE;
1659 command.canvas = canvas;
1660 command.frame = frame;
1661 command.alpha = fade;
1662 send_command(&command);
1665 void Playback3D::do_fade_sync(Playback3DCommand *command)
1668 BC_WindowBase *window =
1669 command->canvas->lock_canvas("Playback3D::do_fade_sync");
1671 window->enable_opengl();
1672 switch( command->frame->get_opengl_state() ) {
1674 command->frame->to_texture();
1677 case VFrame::SCREEN:
1678 // Read back from PBuffer
1679 // Bind context to pbuffer
1680 command->frame->enable_opengl();
1681 command->frame->screen_to_texture();
1685 command->frame->enable_opengl();
1686 command->frame->init_screen();
1687 command->frame->bind_texture(0);
1689 // glClearColor(0.0, 0.0, 0.0, 0.0);
1690 // glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1691 glDisable(GL_BLEND);
1692 unsigned int frag_shader = 0;
1693 switch(command->frame->get_color_model())
1695 // For the alpha colormodels, the native function seems to multiply the
1696 // components by the alpha instead of just the alpha.
1700 frag_shader = VFrame::make_shader(0, fade_rgba_frag, 0);
1705 glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
1706 glColor4f(command->alpha, command->alpha, command->alpha, 1);
1711 frag_shader = VFrame::make_shader(0, fade_yuv_frag, 0);
1717 glUseProgram(frag_shader);
1719 if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0)
1720 glUniform1i(variable, 0);
1721 if((variable = glGetUniformLocation(frag_shader, "alpha")) >= 0)
1722 glUniform1f(variable, command->alpha);
1725 command->frame->draw_texture();
1726 command->frame->set_opengl_state(VFrame::SCREEN);
1733 glColor4f(1, 1, 1, 1);
1734 glDisable(GL_BLEND);
1736 command->canvas->unlock_canvas();
1741 int Playback3D::run_plugin(Canvas *canvas, PluginClient *client)
1743 Playback3DCommand command;
1744 command.command = Playback3DCommand::PLUGIN;
1745 command.canvas = canvas;
1746 command.plugin_client = client;
1747 return send_command(&command);
1750 void Playback3D::run_plugin_sync(Playback3DCommand *command)
1752 BC_WindowBase *window =
1753 command->canvas->lock_canvas("Playback3D::run_plugin_sync");
1755 window->enable_opengl();
1756 command->result = ((PluginVClient*)command->plugin_client)->handle_opengl();
1758 command->canvas->unlock_canvas();