--- /dev/null
+
+/*
+ * CINELERRA
+ * Copyright (C) 2009 Adam Williams <broadcast at earthling dot net>
+ *
+ * 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 "bcsignals.h"
+#include "bcwindowbase.h"
+#include "canvas.h"
+#include "clip.h"
+#include "condition.h"
+#include "maskautos.h"
+#include "maskauto.h"
+#include "mutex.h"
+#include "overlayframe.inc"
+#include "playback3d.h"
+#include "pluginclient.h"
+#include "pluginvclient.h"
+#include "transportque.inc"
+#include "vframe.h"
+
+#ifdef HAVE_GL
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/glu.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+
+// Shaders
+// These should be passed to VFrame::make_shader to construct shaders.
+// Can't hard code sampler2D
+
+
+static const char *yuv_to_rgb_frag =
+ "uniform sampler2D tex;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
+ " yuva.rgb -= vec3(0, 0.5, 0.5);\n"
+ " const mat3 yuv_to_rgb_matrix = mat3(\n"
+ " 1, 1, 1, \n"
+ " 0, -0.34414, 1.77200, \n"
+ " 1.40200, -0.71414, 0);\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"
+ "void main()\n"
+ "{\n"
+ " vec4 yuva = texture2D(tex, gl_TexCoord[0].st);\n"
+ " yuva.rgb -= vec3(0, 0.5, 0.5);\n"
+ " const mat3 yuv_to_rgb_matrix = mat3(\n"
+ " 1, 1, 1, \n"
+ " 0, -0.34414, 1.77200, \n"
+ " 1.40200, -0.71414, 0);\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"
+ "void main()\n"
+ "{\n"
+ " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
+ " const mat3 rgb_to_yuv_matrix = mat3(\n"
+ " 0.29900, -0.16874, 0.50000, \n"
+ " 0.58700, -0.33126, -0.41869, \n"
+ " 0.11400, 0.50000, -0.08131);\n"
+ " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n"
+ " rgba.rgb += vec3(0, 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"
+ "void main()\n"
+ "{\n"
+ " vec4 rgba = texture2D(tex, gl_TexCoord[0].st);\n"
+ " const mat3 rgb_to_yuv_matrix = mat3(\n"
+ " 0.29900, -0.16874, 0.50000, \n"
+ " 0.58700, -0.33126, -0.41869, \n"
+ " 0.11400, 0.50000, -0.08131);\n"
+ " rgba.rgb *= rgba.a;\n"
+ " rgba.a = 1.0;\n"
+ " rgba.rgb = rgb_to_yuv_matrix * rgba.rgb;\n"
+ " rgba.rgb += vec3(0, 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";
+
+// NORMAL
+static const char *blend_normal_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas * (1.0 - gl_FragColor.a) + gl_FragColor * gl_FragColor.a;\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// ADDITION
+static const char *blend_add_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas + gl_FragColor;\n"
+ " result = clamp(result, 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"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = gl_FragColor - canvas;\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// MULTIPLY
+static const char *blend_multiply_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas * gl_FragColor;\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DIVIDE
+static const char *blend_divide_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = gl_FragColor / canvas;\n"
+ " if(!canvas.r) result.r = 1.0;\n"
+ " if(!canvas.g) result.g = 1.0;\n"
+ " if(!canvas.b) result.b = 1.0;\n"
+ " if(!canvas.a) result.a = 1.0;\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// REPLACE
+static const char *blend_replace_frag =
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ "}\n";
+
+// MAX
+static const char *blend_max_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = max(canvas, gl_FragColor);\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// MIN
+static const char *blend_min_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = min(canvas, gl_FragColor);\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// AVERAGE
+static const char *blend_average_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = (canvas + gl_FragColor) * 0.5;\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DARKEN
+static const char *blend_darken_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(canvas.rgb * (1.0 - gl_FragColor.a) +"
+ " gl_FragColor.rgb * (1.0 - canvas.a) +"
+ " min(canvas.rgb, gl_FragColor.rgb), "
+ " canvas.a + gl_FragColor.a - canvas.a * gl_FragColor.a);\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// LIGHTEN
+static const char *blend_lighten_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(canvas.rgb * (1.0 - gl_FragColor.a) +"
+ " gl_FragColor.rgb * (1.0 - canvas.a) +"
+ " max(canvas.rgb, gl_FragColor.rgb), "
+ " canvas.a + gl_FragColor.a - canvas.a * gl_FragColor.a);\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DST
+static const char *blend_dst_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+// " gl_FragColor.a *= alpha;\n"
+// " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+// " vec4 result = canvas;\n"
+// " gl_FragColor = mix(result, canvas, alpha);\n"
+ " gl_FragColor = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ "}\n";
+
+// DST_ATOP
+static const char *blend_dst_atop_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(canvas.rgb * gl_FragColor.a + "
+ "(1.0 - canvas.a) * gl_FragColor.rgb, gl_FragColor.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DST_IN
+static const char *blend_dst_in_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas * gl_FragColor.a;\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DST_OUT
+static const char *blend_dst_out_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas * (1.0 - gl_FragColor.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// DST_OVER
+static const char *blend_dst_over_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(canvas.rgb + (1.0 - canvas.a) * gl_FragColor.rgb, "
+ " gl_FragColor.a + canvas.a - gl_FragColor.a * canvas.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// SRC
+static const char *blend_src_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = gl_FragColor;\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// SRC_ATOP
+static const char *blend_src_atop_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(gl_FragColor.rgb * canvas.a + "
+ "canvas.rgb * (1.0 - gl_FragColor.a), canvas.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// SRC_IN
+static const char *blend_src_in_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = gl_FragColor * canvas.a;\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// SRC_OUT
+static const char *blend_src_out_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = gl_FragColor * (1.0 - canvas.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// SRC_OVER
+static const char *blend_src_over_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(gl_FragColor.rgb + (1.0 - gl_FragColor.a) * canvas.rgb, "
+ "gl_FragColor.a + canvas.a - gl_FragColor.a * canvas.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// OR
+static const char *blend_or_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = canvas + gl_FragColor - canvas * gl_FragColor;\n"
+ " result = clamp(result, 0.0, 1.0);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+// XOR
+static const char *blend_xor_frag =
+ "uniform sampler2D tex2;\n"
+ "uniform vec2 tex2_dimensions;\n"
+ "uniform float alpha;\n"
+ "void main() {\n"
+ " gl_FragColor.a *= alpha;\n"
+ " vec4 canvas = texture2D(tex2, gl_FragCoord.xy / tex2_dimensions);\n"
+ " vec4 result = vec4(gl_FragColor.rgb * (1.0 - canvas.a) + "
+ "(1.0 - gl_FragColor.a) * canvas.rgb, "
+ "gl_FragColor.a + canvas.a - 2.0 * gl_FragColor.a * canvas.a);\n"
+ " gl_FragColor = mix(canvas, result, alpha);\n"
+ "}\n";
+
+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 *multiply_mask4_frag =
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "uniform float scale;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
+ " gl_FragColor.a *= texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).r;\n"
+ "}\n";
+
+static const char *multiply_mask3_frag =
+ "uniform sampler2D tex;\n"
+ "uniform sampler2D tex1;\n"
+ "uniform float scale;\n"
+ "uniform bool is_yuv;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
+ " float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).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"
+ "uniform float scale;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = texture2D(tex, gl_TexCoord[0].st);\n"
+ " float a = texture2D(tex1, gl_TexCoord[0].st / vec2(scale, scale)).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";
+
+
+
+
+
+
+
+
+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);
+}
+
+
+///static void glDebugCallback(GLenum src, GLenum typ, GLuint id,
+/// GLenum svy, GLsizei len, const GLchar* msg, void* dat)
+//static void glDebugCallback(unsigned int src, unsigned int typ, unsigned int id,
+// unsigned int svy, int len, const char* msg, const void* dat)
+//{
+// printf("glDebug: %d:%d; %d/%d %s\n",src,typ,id,svy,msg);
+//}
+
+
+Playback3D::Playback3D(MWindow *mwindow)
+ : BC_Synchronous()
+{
+ this->mwindow = mwindow;
+ temp_texture = 0;
+ //Enabling OpenGL debug output on nVidia drivers
+// glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE);
+// glEnable(GL_DEBUG_OUTPUT);
+// glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+// glDebugMessageCallback(glDebugCallback, 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
+ command->canvas->lock_canvas("Playback3D::draw_refresh_sync");
+ BC_WindowBase *window = command->canvas->get_canvas();
+ if(window)
+ {
+ window->lock_window("Playback3D:draw_refresh_sync");
+ window->enable_opengl();
+ 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());
+// 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
+// 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->enable_opengl();
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE,
+ command->frame->get_rows()[0]);
+ command->frame->flip_vert();
+ command->frame->set_opengl_state(VFrame::RAM);
+ }
+ }
+ 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());
+ }
+
+ window->unlock_window();
+ }
+ 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)
+// {
+// command->canvas->lock_canvas("Playback3D::draw_refresh_sync");
+// BC_WindowBase *window = command->canvas->get_canvas();
+// if(window)
+// {
+// window->lock_window("Playback3D:draw_refresh_sync");
+// 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->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);
+//
+// window->unlock_window();
+// }
+// command->canvas->unlock_canvas();
+// }
+
+
+
+
+
+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)
+{
+ command->canvas->lock_canvas("Playback3D::write_buffer_sync");
+ if(command->canvas->get_canvas())
+ {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::write_buffer_sync");
+// Update hidden cursor
+ window->update_video_cursor();
+// Make sure OpenGL is enabled first.
+ window->enable_opengl();
+
+
+//printf("Playback3D::write_buffer_sync 1 %d\n", window->get_id());
+ switch(command->frame->get_opengl_state())
+ {
+// Upload texture and composite to screen
+ case VFrame::RAM:
+ command->frame->to_texture();
+ draw_output(command);
+ break;
+// Composite texture to screen and swap buffer
+ case VFrame::TEXTURE:
+ draw_output(command);
+ break;
+ case VFrame::SCREEN:
+// swap buffers only
+ window->flip_opengl();
+ break;
+ default:
+ printf("Playback3D::write_buffer_sync unknown state\n");
+ break;
+ }
+ window->unlock_window();
+ }
+
+ command->canvas->unlock_canvas();
+}
+
+
+
+void Playback3D::draw_output(Playback3DCommand *command)
+{
+#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);
+
+ if(!command->is_cleared)
+ {
+// If we get here, the virtual console was not used.
+ init_frame(command);
+ }
+
+// Texture
+// Undo any previous shader settings
+ command->frame->bind_texture(0);
+
+
+
+
+// Convert colormodel
+ unsigned int frag_shader = 0;
+ switch(command->frame->get_color_model())
+ {
+ case BC_YUV888:
+ case BC_YUVA8888:
+ frag_shader = VFrame::make_shader(0,
+ yuv_to_rgb_frag,
+ 0);
+ break;
+ }
+
+
+ if(frag_shader > 0)
+ {
+ glUseProgram(frag_shader);
+ int variable = glGetUniformLocation(frag_shader, "tex");
+// Set texture unit of the texture
+ glUniform1i(variable, 0);
+ }
+
+ if(BC_CModels::components(command->frame->get_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,
+ 1);
+
+
+// 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::init_frame(Playback3DCommand *command)
+{
+#ifdef HAVE_GL
+ canvas_w = command->canvas->get_canvas()->get_w();
+ canvas_h = command->canvas->get_canvas()->get_h();
+
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ 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)
+{
+ command->canvas->lock_canvas("Playback3D::clear_output_sync");
+ if(command->canvas->get_canvas())
+ {
+ command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync");
+// If we get here, the virtual console is being used.
+ command->canvas->get_canvas()->enable_opengl();
+
+// Using pbuffer for refresh frame.
+ if(command->frame)
+ {
+ command->frame->enable_opengl();
+ }
+
+
+ init_frame(command);
+ command->canvas->get_canvas()->unlock_window();
+ }
+ command->canvas->unlock_canvas();
+}
+
+
+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)
+{
+ command->canvas->lock_canvas("Playback3D::clear_output_sync");
+ if(command->canvas->get_canvas())
+ {
+ command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync");
+ command->canvas->get_canvas()->enable_opengl();
+ command->frame->enable_opengl();
+ command->frame->clear_pbuffer();
+ command->frame->set_opengl_state(VFrame::SCREEN);
+ command->canvas->get_canvas()->unlock_window();
+ }
+ command->canvas->unlock_canvas();
+}
+
+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)
+{
+ command->canvas->lock_canvas("Playback3D::do_camera_sync");
+ if(command->canvas->get_canvas())
+ {
+ command->canvas->get_canvas()->lock_window("Playback3D::clear_output_sync");
+ 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->canvas->get_canvas()->unlock_window();
+ }
+ command->canvas->unlock_canvas();
+}
+
+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_add_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_average_frag, // TRANSFER_AVERAGE
+ 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_or_frag, // TRANSFER_OR
+ blend_xor_frag // TRANSFER_XOR
+ };
+
+ command->canvas->lock_canvas("Playback3D::overlay_sync");
+ if(command->canvas->get_canvas()) {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::overlay_sync");
+// Make sure OpenGL is enabled first.
+ window->enable_opengl();
+ window->update_video_cursor();
+
+ glColor4f(1, 1, 1, 1);
+ glDisable(GL_BLEND);
+
+ 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
+ canvas_w = window->get_w();
+ canvas_h = window->get_h();
+ }
+
+
+//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();
+ if(command->frame)
+ command->frame->enable_opengl();
+ else
+ window->enable_opengl();
+ break;
+ default:
+ printf("Playback3D::overlay_sync unknown state\n");
+ break;
+ }
+
+
+ const char *shader_stack[4] = { 0, 0, 0, 0, };
+ int total_shaders = 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
+ switch(command->input->get_color_model()) {
+ case BC_YUV888:
+ case BC_YUVA8888:
+ shader_stack[total_shaders++] = yuv_to_rgb_frag;
+ break;
+ }
+ }
+
+// 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
+ unsigned int frag_shader = 0;
+ if(shader_stack[0]) {
+ frag_shader = VFrame::make_shader(0,
+ shader_stack[0], shader_stack[1],
+ shader_stack[2], shader_stack[3], 0);
+
+ glUseProgram(frag_shader);
+
+// Set texture unit of the texture
+ glUniform1i(glGetUniformLocation(frag_shader, "tex"), 0);
+// Set texture unit of the temp texture
+ glUniform1i(glGetUniformLocation(frag_shader, "tex2"), 1);
+// Set alpha
+ int variable = glGetUniformLocation(frag_shader, "alpha");
+ glUniform1f(variable, command->alpha);
+// Set dimensions of the temp texture
+ if(temp_texture)
+ glUniform2f(glGetUniformLocation(frag_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,
+// Don't flip vertical if nested
+ command->is_nested > 0 ? 0 : 1);
+ 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);
+
+ window->unlock_window();
+ }
+ 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);
+}
+
+
+
+#ifdef HAVE_GL
+struct Vertex : ListItem<Vertex>
+{
+ GLdouble c[3];
+};
+// this list is only used from the main thread, no locking needed
+// this must be a list so that pointers to allocated entries remain valid
+// when new entries are added
+static List<Vertex> *vertex_cache = 0;
+
+static void combine_callback(GLdouble coords[3],
+ GLdouble *vertex_data[4],
+ GLfloat weight[4],
+ GLdouble **dataOut)
+{
+// can't use malloc here; GLU doesn't delete the memory for us!
+ Vertex* vertex = vertex_cache->append();
+ vertex->c[0] = coords[0];
+ vertex->c[1] = coords[1];
+ vertex->c[2] = coords[2];
+// we don't need to interpolate anything
+
+ *dataOut = &vertex->c[0];
+}
+#endif
+
+
+void Playback3D::do_mask_sync(Playback3DCommand *command)
+{
+#ifdef HAVE_GL
+ command->canvas->lock_canvas("Playback3D::do_mask_sync");
+ if(command->canvas->get_canvas())
+ {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::do_mask_sync");
+ window->enable_opengl();
+
+ switch(command->frame->get_opengl_state())
+ {
+ case VFrame::RAM:
+// Time to upload 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;
+ }
+
+
+
+// Create PBuffer and draw the mask on it
+ command->frame->enable_opengl();
+
+// Initialize coordinate system
+ int w = command->frame->get_w();
+ int h = command->frame->get_h();
+ command->frame->init_screen();
+
+// Clear screen
+ glDisable(GL_TEXTURE_2D);
+ if(command->default_auto->mode == MASK_MULTIPLY_ALPHA)
+ {
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glColor4f((float)command->keyframe->value / 100,
+ (float)command->keyframe->value / 100,
+ (float)command->keyframe->value / 100,
+ 1.0);
+ }
+ else
+ {
+ glClearColor(1.0, 1.0, 1.0, 1.0);
+ glColor4f((float)1.0 - (float)command->keyframe->value / 100,
+ (float)1.0 - (float)command->keyframe->value / 100,
+ (float)1.0 - (float)command->keyframe->value / 100,
+ 1.0);
+ }
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+
+// Draw mask with scaling to simulate feathering
+ GLUtesselator *tesselator = gluNewTess();
+ gluTessProperty(tesselator, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
+ gluTessCallback(tesselator, GLU_TESS_VERTEX, (GLvoid (*) ( )) &glVertex3dv);
+ gluTessCallback(tesselator, GLU_TESS_BEGIN, (GLvoid (*) ( )) &glBegin);
+ gluTessCallback(tesselator, GLU_TESS_END, (GLvoid (*) ( )) &glEnd);
+ gluTessCallback(tesselator, GLU_TESS_COMBINE, (GLvoid (*) ( ))&combine_callback);
+
+ vertex_cache = new List<Vertex>;
+
+
+// Draw every submask as a new polygon
+ int total_submasks = command->keyframe_set->total_submasks(
+ command->start_position_project,
+ PLAY_FORWARD);
+ float scale = command->keyframe->feather + 1;
+ int display_list = glGenLists(1);
+ glNewList(display_list, GL_COMPILE);
+ for(int k = 0; k < total_submasks; k++)
+ {
+ gluTessBeginPolygon(tesselator, NULL);
+ gluTessBeginContour(tesselator);
+ ArrayList<MaskPoint*> *points = new ArrayList<MaskPoint*>;
+ command->keyframe_set->get_points(points,
+ k,
+ command->start_position_project,
+ PLAY_FORWARD);
+
+ int first_point = 0;
+// Need to tabulate every vertex in persistent memory because
+// gluTessVertex doesn't copy them.
+ ArrayList<GLdouble*> coords;
+ for(int i = 0; i < points->total; i++)
+ {
+ MaskPoint *point1 = points->values[i];
+ MaskPoint *point2 = (i >= points->total - 1) ?
+ points->values[0] :
+ points->values[i + 1];
+
+ float x, y;
+ int segments = 0;
+ if(point1->control_x2 == 0 &&
+ point1->control_y2 == 0 &&
+ point2->control_x1 == 0 &&
+ point2->control_y1 == 0)
+ segments = 1;
+
+ float x0 = point1->x;
+ float y0 = point1->y;
+ float x1 = point1->x + point1->control_x2;
+ float y1 = point1->y + point1->control_y2;
+ float x2 = point2->x + point2->control_x1;
+ float y2 = point2->y + point2->control_y1;
+ float x3 = point2->x;
+ float y3 = point2->y;
+
+ // forward differencing bezier curves implementation taken from GPL code at
+ // http://cvs.sourceforge.net/viewcvs.py/guliverkli/guliverkli/src/subtitles/Rasterizer.cpp?rev=1.3
+
+ float cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0;
+
+ // [-1 +3 -3 +1]
+ // [+3 -6 +3 0]
+ // [-3 +3 0 0]
+ // [+1 0 0 0]
+
+ cx3 = - x0 + 3*x1 - 3*x2 + x3;
+ cx2 = 3*x0 - 6*x1 + 3*x2;
+ cx1 = -3*x0 + 3*x1;
+ cx0 = x0;
+
+ cy3 = - y0 + 3*y1 - 3*y2 + y3;
+ cy2 = 3*y0 - 6*y1 + 3*y2;
+ cy1 = -3*y0 + 3*y1;
+ cy0 = y0;
+
+ // This equation is from Graphics Gems I.
+ //
+ // The idea is that since we're approximating a cubic curve with lines,
+ // any error we incur is due to the curvature of the line, which we can
+ // estimate by calculating the maximum acceleration of the curve. For
+ // a cubic, the acceleration (second derivative) is a line, meaning that
+ // the absolute maximum acceleration must occur at either the beginning
+ // (|c2|) or the end (|c2+c3|). Our bounds here are a little more
+ // conservative than that, but that's okay.
+ if (segments == 0)
+ {
+ float maxaccel1 = fabs(2*cy2) + fabs(6*cy3);
+ float maxaccel2 = fabs(2*cx2) + fabs(6*cx3);
+
+ float maxaccel = maxaccel1 > maxaccel2 ? maxaccel1 : maxaccel2;
+ float h = 1.0;
+
+ if(maxaccel > 8.0) h = sqrt((8.0) / maxaccel);
+ segments = int(1/h);
+ }
+
+ for(int j = 0; j <= segments; j++)
+ {
+ float t = (float)j / segments;
+ x = cx0 + t*(cx1 + t*(cx2 + t*cx3));
+ y = cy0 + t*(cy1 + t*(cy2 + t*cy3));
+
+ if(j > 0 || first_point)
+ {
+ GLdouble *coord = new GLdouble[3];
+ coord[0] = x / scale;
+ coord[1] = -h + y / scale;
+ coord[2] = 0;
+ coords.append(coord);
+ first_point = 0;
+ }
+ }
+ }
+
+// Now that we know the total vertices, send them to GLU
+ for(int i = 0; i < coords.total; i++)
+ gluTessVertex(tesselator, coords.values[i], coords.values[i]);
+
+ gluTessEndContour(tesselator);
+ gluTessEndPolygon(tesselator);
+ points->remove_all_objects();
+ delete points;
+ coords.remove_all_objects();
+ }
+ glEndList();
+ glCallList(display_list);
+ glDeleteLists(display_list, 1);
+ gluDeleteTess(tesselator);
+
+ delete vertex_cache;
+ vertex_cache = 0;
+
+ glColor4f(1, 1, 1, 1);
+
+
+// Read mask into temporary texture.
+// For feathering, just read the part of the screen after the downscaling.
+
+
+ float w_scaled = w / scale;
+ float h_scaled = h / scale;
+// Don't vary the texture size according to scaling because that
+// would waste memory.
+// This enables and binds the temporary texture.
+ glActiveTexture(GL_TEXTURE1);
+ BC_Texture::new_texture(&temp_texture,
+ w,
+ h,
+ command->frame->get_color_model());
+ temp_texture->bind(1);
+ glReadBuffer(GL_BACK);
+
+// Need to add extra size to fill in the bottom right
+ glCopyTexSubImage2D(GL_TEXTURE_2D,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ (int)MIN(w_scaled + 2, w),
+ (int)MIN(h_scaled + 2, h));
+
+ command->frame->bind_texture(0);
+
+
+// For feathered masks, use a shader to multiply.
+// For unfeathered masks, we could use a stencil buffer
+// for further optimization but we also need a YUV algorithm.
+ unsigned int frag_shader = 0;
+ switch(temp_texture->get_texture_components())
+ {
+ case 3:
+ if(command->frame->get_color_model() == BC_YUV888)
+ frag_shader = VFrame::make_shader(0,
+ multiply_yuvmask3_frag,
+ 0);
+ else
+ frag_shader = VFrame::make_shader(0,
+ multiply_mask3_frag,
+ 0);
+ break;
+ case 4:
+ frag_shader = VFrame::make_shader(0,
+ multiply_mask4_frag,
+ 0);
+ break;
+ }
+
+ if(frag_shader)
+ {
+ int variable;
+ glUseProgram(frag_shader);
+ if((variable = glGetUniformLocation(frag_shader, "tex")) >= 0)
+ glUniform1i(variable, 0);
+ if((variable = glGetUniformLocation(frag_shader, "tex1")) >= 0)
+ glUniform1i(variable, 1);
+ if((variable = glGetUniformLocation(frag_shader, "scale")) >= 0)
+ glUniform1f(variable, scale);
+ }
+
+
+
+// Write texture to PBuffer with multiply and scaling for feather.
+
+
+ command->frame->draw_texture(0, 0, w, h, 0, 0, w, h);
+ command->frame->set_opengl_state(VFrame::SCREEN);
+
+
+// Disable temp texture
+ glUseProgram(0);
+
+ glActiveTexture(GL_TEXTURE1);
+ glDisable(GL_TEXTURE_2D);
+ delete temp_texture;
+ temp_texture = 0;
+
+ glActiveTexture(GL_TEXTURE0);
+ glDisable(GL_TEXTURE_2D);
+
+// Default drawable
+ window->enable_opengl();
+ window->unlock_window();
+ }
+ 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
+ command->canvas->lock_canvas("Playback3D::convert_cmodel_sync");
+
+ if(command->canvas->get_canvas())
+ {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::convert_cmodel_sync");
+ window->enable_opengl();
+
+// Import into hardware
+ command->frame->enable_opengl();
+ command->frame->init_screen();
+ command->frame->to_texture();
+
+// Colormodel permutation
+ const char *shader = 0;
+ int src_cmodel = command->frame->get_color_model();
+ int dst_cmodel = command->dst_cmodel;
+ typedef struct
+ {
+ int src;
+ int dst;
+ const char *shader;
+ } cmodel_shader_table_t;
+ static cmodel_shader_table_t cmodel_shader_table[] =
+ {
+ { BC_RGB888, BC_YUV888, rgb_to_yuv_frag },
+ { BC_RGB888, BC_YUVA8888, rgb_to_yuv_frag },
+ { BC_RGBA8888, BC_RGB888, rgba_to_rgb_frag },
+ { BC_RGBA8888, BC_RGB_FLOAT, rgba_to_rgb_frag },
+ { BC_RGBA8888, BC_YUV888, rgba_to_yuv_frag },
+ { BC_RGBA8888, BC_YUVA8888, rgb_to_yuv_frag },
+ { BC_RGB_FLOAT, BC_YUV888, rgb_to_yuv_frag },
+ { BC_RGB_FLOAT, BC_YUVA8888, rgb_to_yuv_frag },
+ { BC_RGBA_FLOAT, BC_RGB888, rgba_to_rgb_frag },
+ { BC_RGBA_FLOAT, BC_RGB_FLOAT, rgba_to_rgb_frag },
+ { BC_RGBA_FLOAT, BC_YUV888, rgba_to_yuv_frag },
+ { BC_RGBA_FLOAT, BC_YUVA8888, rgb_to_yuv_frag },
+ { BC_YUV888, BC_RGB888, yuv_to_rgb_frag },
+ { BC_YUV888, BC_RGBA8888, yuv_to_rgb_frag },
+ { BC_YUV888, BC_RGB_FLOAT, yuv_to_rgb_frag },
+ { BC_YUV888, BC_RGBA_FLOAT, yuv_to_rgb_frag },
+ { BC_YUVA8888, BC_RGB888, yuva_to_rgb_frag },
+ { BC_YUVA8888, BC_RGBA8888, yuv_to_rgb_frag },
+ { BC_YUVA8888, BC_RGB_FLOAT, yuva_to_rgb_frag },
+ { BC_YUVA8888, BC_RGBA_FLOAT, yuv_to_rgb_frag },
+ { BC_YUVA8888, BC_YUV888, yuva_to_yuv_frag },
+ };
+
+ int table_size = sizeof(cmodel_shader_table) / sizeof(cmodel_shader_table_t);
+ for(int i = 0; i < table_size; i++)
+ {
+ if(cmodel_shader_table[i].src == src_cmodel &&
+ cmodel_shader_table[i].dst == dst_cmodel)
+ {
+ shader = cmodel_shader_table[i].shader;
+ break;
+ }
+ }
+
+// printf("Playback3D::convert_cmodel_sync %d %d %d shader=\n%s",
+// __LINE__,
+// command->frame->get_color_model(),
+// command->dst_cmodel,
+// shader);
+
+ if(shader)
+ {
+//printf("Playback3D::convert_cmodel_sync %d\n", __LINE__);
+ command->frame->bind_texture(0);
+ unsigned int shader_id = -1;
+ if(shader)
+ {
+ shader_id = VFrame::make_shader(0,
+ shader,
+ 0);
+ glUseProgram(shader_id);
+ glUniform1i(glGetUniformLocation(shader_id, "tex"), 0);
+ }
+
+ command->frame->draw_texture();
+
+ if(shader) glUseProgram(0);
+
+ command->frame->set_opengl_state(VFrame::SCREEN);
+ }
+
+ window->unlock_window();
+ }
+
+ 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
+ command->canvas->lock_canvas("Playback3D::do_mask_sync");
+ if(command->canvas->get_canvas())
+ {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::do_fade_sync");
+ 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);
+
+ window->unlock_window();
+ }
+ 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)
+{
+ command->canvas->lock_canvas("Playback3D::run_plugin_sync");
+ if(command->canvas->get_canvas())
+ {
+ BC_WindowBase *window = command->canvas->get_canvas();
+ window->lock_window("Playback3D::run_plugin_sync");
+ window->enable_opengl();
+
+ command->result = ((PluginVClient*)command->plugin_client)->handle_opengl();
+
+ window->unlock_window();
+ }
+ command->canvas->unlock_canvas();
+}
+
+