
/*
 * CINELERRA
 * Copyright (C) 2008 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
 *
 */

#include "bcbitmap.h"
#include "bcpixmap.h"
#include "bcresources.h"
#include "bcsignals.h"
#include "bcsynchronous.h"
#include "bcwindowbase.h"
#include "vframe.h"
#include "workarounds.h"

#include <unistd.h>

BC_Pixmap::BC_Pixmap(BC_WindowBase *parent_window,
	VFrame *frame,
	int mode,
	int icon_offset)
{
	reset();

	BC_Bitmap *opaque_bitmap = 0, *alpha_bitmap = 0;
	if(frame->get_color_model() != BC_RGBA8888 && mode == PIXMAP_ALPHA)
		mode = PIXMAP_OPAQUE;
	this->mode = mode;

// Temporary bitmaps
	if(use_opaque())
	{
		opaque_bitmap = new BC_Bitmap(parent_window,
			frame->get_w(), frame->get_h(),
			parent_window->get_color_model(), 0);
		opaque_bitmap->read_frame(frame,
			0, 0, frame->get_w(), frame->get_h());

	}

	if(use_alpha())
	{
		alpha_bitmap = new BC_Bitmap(parent_window,
				frame->get_w(),
				frame->get_h(),
				BC_TRANSPARENCY,
				0);

		if(frame->get_color_model() != BC_RGBA8888)
			printf("BC_Pixmap::BC_Pixmap: PIXMAP_ALPHA but frame doesn't have alpha.\n");
		alpha_bitmap->read_frame(frame,
			0,
			0,
			frame->get_w(),
			frame->get_h());
	}

	initialize(parent_window,
		frame->get_w(),
		frame->get_h(),
		mode);

	if(use_opaque())
	{
		opaque_bitmap->write_drawable(opaque_pixmap,
			top_level->gc, 0, 0, 0, 0, w, h, 1);
		delete opaque_bitmap;
	}

	if(use_alpha())
	{
		alpha_bitmap->write_drawable(alpha_pixmap,
			copy_gc, 0, 0, icon_offset ? 2 : 0, icon_offset ? 2 : 0, w, h, 1);
		delete alpha_bitmap;
		XFreeGC(top_level->display, copy_gc);

		XSetClipMask(top_level->display, alpha_gc, alpha_pixmap);
	}
}

BC_Pixmap::BC_Pixmap(BC_WindowBase *parent_window, int w, int h)
{
	reset();
	initialize(parent_window, w, h, PIXMAP_OPAQUE);
}


BC_Pixmap::~BC_Pixmap()
{
	if(use_opaque())
	{
#ifdef HAVE_XFT
		if(opaque_xft_draw)
			xftDrawDestroy((XftDraw*)opaque_xft_draw);
#endif
		XFreePixmap(top_level->display, opaque_pixmap);
	}

	if(use_alpha())
	{
		XFreeGC(top_level->display, alpha_gc);
#ifdef HAVE_XFT
		if(alpha_xft_draw)
			xftDrawDestroy((XftDraw*)alpha_xft_draw);
#endif
		XFreePixmap(top_level->display, alpha_pixmap);
	}


// Have to delete GL objects because pixmaps are deleted during resizes.
#ifdef HAVE_GL
	if(BC_WindowBase::get_synchronous() && glx_pixmap)
	{
		BC_WindowBase::get_synchronous()->delete_pixmap(parent_window,
			glx_pixmap, glx_pixmap_context);
	}
#endif
}


void BC_Pixmap::reset()
{
	parent_window = 0;
	top_level = 0;
	opaque_pixmap = 0;
	alpha_pixmap = 0;
	opaque_xft_draw = 0;
	alpha_xft_draw = 0;
#ifdef HAVE_GL
	glx_pixmap_context = 0;
	glx_pixmap = 0;
#endif
}

int BC_Pixmap::initialize(BC_WindowBase *parent_window, int w, int h, int mode)
{
	if( w < 1 ) w = 1;
	if( h < 1 ) h = 1;
	this->w = w;
	this->h = h;
	this->parent_window = parent_window;
	this->mode = mode;
	top_level = parent_window->top_level;

	if(use_opaque())
	{
		opaque_pixmap = XCreatePixmap(top_level->display,
			top_level->win,
			w,
			h,
			top_level->default_depth);
#ifdef HAVE_XFT
		if(BC_WindowBase::get_resources()->use_xft)
		{
			opaque_xft_draw = xftDrawCreate(top_level->display,
		    	   opaque_pixmap,
		    	   top_level->vis,
		    	   top_level->cmap);
		}
#endif
	}

	if(use_alpha())
	{
		unsigned long gcmask = GCGraphicsExposures |
			GCForeground |
			GCBackground |
			GCFunction;
		XGCValues gcvalues;
		gcvalues.graphics_exposures = 0;        // prevent expose events for every redraw
		gcvalues.foreground = 0;
		gcvalues.background = 1;
		gcvalues.function = GXcopy;

		alpha_pixmap = XCreatePixmap(top_level->display,
			top_level->win, w, h, 1);
		alpha_gc = XCreateGC(top_level->display,
			top_level->win, gcmask, &gcvalues);
		copy_gc = XCreateGC(top_level->display,
			alpha_pixmap, gcmask, &gcvalues);

#ifdef HAVE_XFT
		if(BC_WindowBase::get_resources()->use_xft)
		{
			alpha_xft_draw = xftDrawCreateBitmap(top_level->display,
				alpha_pixmap);
		}
#endif
	}



// // For some reason, this is required in 32 bit.
// #ifdef HAVE_XFT
// 	if(BC_WindowBase::get_resources()->use_xft)
// 		XSync(top_level->display, False);
// #endif

	return 0;
}

void BC_Pixmap::resize(int w, int h)
{
	Pixmap new_pixmap = XCreatePixmap(top_level->display,
			top_level->win,
			w,
			h,
			top_level->default_depth);
#ifdef HAVE_XFT
	XftDraw *new_xft_draw = 0;
	if(BC_WindowBase::get_resources()->use_xft) {
		new_xft_draw = xftDrawCreate(top_level->display,
		       new_pixmap,
		       top_level->vis,
		       top_level->cmap);
	}
#endif




	XCopyArea(top_level->display,
		opaque_pixmap,
		new_pixmap,
		top_level->gc,
		0,
		0,
		get_w(),
		get_h(),
		0,
		0);
	this->w = w;
	this->h = h;
#ifdef HAVE_XFT
	if(BC_WindowBase::get_resources()->use_xft)
		xftDrawDestroy((XftDraw*)opaque_xft_draw);
#endif
	XFreePixmap(top_level->display, opaque_pixmap);

	opaque_pixmap = new_pixmap;
#ifdef HAVE_XFT
	if(BC_WindowBase::get_resources()->use_xft)
		opaque_xft_draw = new_xft_draw;
#endif
}


void BC_Pixmap::copy_area(int x, int y, int w, int h, int x2, int y2)
{
	XCopyArea(top_level->display,
		opaque_pixmap, opaque_pixmap, top_level->gc,
		x, y, w, h, x2, y2);
}

int BC_Pixmap::write_drawable(Drawable &pixmap,
	int dest_x, int dest_y, int dest_w, int dest_h,
	int src_x, int src_y)
{
//printf("BC_Pixmap::write_drawable 1\n");
	if(dest_w < 0)
	{
		dest_w = w;
		src_x = 0;
	}

	if(dest_h < 0)
	{
		dest_h = h;
		src_y = 0;
	}

	if(use_alpha())
	{
		XSetClipOrigin(top_level->display, alpha_gc, dest_x - src_x, dest_y - src_y);
		XCopyArea(top_level->display,
			this->opaque_pixmap, pixmap, alpha_gc,
			src_x, src_y, dest_w, dest_h, dest_x, dest_y);
	}
	else
	if(use_opaque())
	{
		XCopyArea(top_level->display,
			this->opaque_pixmap, pixmap, top_level->gc,
			src_x, src_y, dest_w, dest_h, dest_x, dest_y);
	}
//printf("BC_Pixmap::write_drawable 2\n");

	return 0;
}

void BC_Pixmap::draw_vframe(VFrame *frame,
	int dest_x, int dest_y, int dest_w, int dest_h,
	int src_x, int src_y)
{
	parent_window->draw_vframe(frame,
		dest_x, dest_y, dest_w, dest_h, src_x,
		src_y, 0, 0, this);
}

void BC_Pixmap::draw_pixmap(BC_Pixmap *pixmap,
	int dest_x, int dest_y, int dest_w, int dest_h,
	int src_x, int src_y)
{
	pixmap->write_drawable(this->opaque_pixmap,
		dest_x, dest_y, dest_w, dest_h,
		src_x, src_y);
}


int BC_Pixmap::get_w() { return w; }
int BC_Pixmap::get_h() { return h; }
int BC_Pixmap::get_w_fixed() { return w - 1; }
int BC_Pixmap::get_h_fixed() { return h - 1; }
Pixmap BC_Pixmap::get_pixmap() { return opaque_pixmap; }
Pixmap BC_Pixmap::get_alpha() { return alpha_pixmap; }
int BC_Pixmap::use_opaque() { return 1; }
int BC_Pixmap::use_alpha() { return mode == PIXMAP_ALPHA; }


void BC_Pixmap::enable_opengl()
{
//printf("BC_Pixmap::enable_opengl called but it doesn't work.\n");
#ifdef HAVE_GL
	BC_WindowBase *current_window = BC_WindowBase::get_synchronous()->current_window;
	if(!glx_pixmap_context) {
		GLXFBConfig *glx_fb_configs = current_window->glx_pixmap_fb_configs();
		if( glx_fb_configs ) {
			Display *dpy = current_window->get_display();
			glx_pixmap =
				glXCreatePixmap(dpy, *glx_fb_configs, opaque_pixmap, 0);
			glx_pixmap_context =
				glXCreateNewContext(dpy, *glx_fb_configs, GLX_RGBA_TYPE, 0, True);
		}
	}
	if( !glx_pixmap )
		printf("BC_Pixmap::enable_opengl(): no glx_pixmap\n");
	else if( !glx_pixmap_context )
		printf("BC_Pixmap::enable_opengl(): no glx_pixmap_context\n");
	else
		current_window->glx_make_current(glx_pixmap, glx_pixmap_context);
#endif
}

