示例#1
0
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::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();
}
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::overlay_sync(Playback3DCommand *command)
{
#ifdef HAVE_GL
	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();


// Render to PBuffer
		if(command->frame)
		{
			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();
		}

		glColor4f(1, 1, 1, 1);

//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[3] = { 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)
		{
			switch(command->input->get_color_model())
			{
				case BC_YUV888:
				case BC_YUVA8888:
					shader_stack[total_shaders++] = yuv_to_rgb_frag;
					break;
			}
		}

// Change blend operation
		switch(command->mode)
		{
			case TRANSFER_NORMAL:
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				break;

			case TRANSFER_REPLACE:
// This requires overlaying an alpha multiplied image on a black screen.
				glDisable(GL_BLEND);
				if(command->input->get_texture_components() == 4)
				{
					if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
					shader_stack[total_shaders++] = multiply_alpha_frag;
				}
				break;

// To do these operations, we need to copy the input buffer to a texture
// and blend 2 textures in another shader
			case TRANSFER_ADDITION:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_add_frag;
				break;
			case TRANSFER_SUBTRACT:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_subtract_frag;
				break;
			case TRANSFER_MULTIPLY:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_multiply_frag;
				break;
			case TRANSFER_MAX:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_max_frag;
				break;
			case TRANSFER_MIN:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_min_frag;
				break;
			case TRANSFER_DIVIDE:
				enable_overlay_texture(command);
				if(!total_shaders) shader_stack[total_shaders++] = read_texture_frag;
				shader_stack[total_shaders++] = blend_divide_frag;
				break;
		}

		unsigned int frag_shader = 0;
		if(shader_stack[0]) 
		{
			frag_shader = VFrame::make_shader(0,
				shader_stack[0],
				shader_stack[1],
				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 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);

		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::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
}
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 },
		};

		for(int i = 0; i < sizeof(cmodel_shader_table) / sizeof(cmodel_shader_table_t); 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_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();
		int value = command->keyframe_set->get_value(command->start_position_project,
			PLAY_FORWARD);
		float feather = command->keyframe_set->get_feather(command->start_position_project,
			PLAY_FORWARD);

// 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)value / 100, 
				(float)value / 100, 
				(float)value / 100, 
				1.0);
		}
		else
		{
			glClearColor(1.0, 1.0, 1.0, 1.0);
			glColor4f((float)1.0 - (float)value / 100, 
				(float)1.0 - (float)value / 100, 
				(float)1.0 - (float)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);


// Draw every submask as a new polygon
		int total_submasks = command->keyframe_set->total_submasks(
			command->start_position_project, 
			PLAY_FORWARD);
		float scale = 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];


// This is very slow.
				float x, y;
				int segments = (int)(sqrt(SQR(point1->x - point2->x) + SQR(point1->y - point2->y)));
				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;

				for(int j = 0; j <= segments; j++)
				{
					float t = (float)j / segments;
					float tpow2 = t * t;
					float tpow3 = t * t * t;
					float invt = 1 - t;
					float invtpow2 = invt * invt;
					float invtpow3 = invt * invt * invt;

					x = (        invtpow3 * x0
						+ 3 * t     * invtpow2 * x1
						+ 3 * tpow2 * invt     * x2 
						+     tpow3            * x3);
					y = (        invtpow3 * y0 
						+ 3 * t     * invtpow2 * y1
						+ 3 * tpow2 * invt     * y2 
						+     tpow3            * y3);


					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);

		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
}
示例#8
0
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
}