//*****************************************************************************************
// Truevision - a 3d modeler for gnome and povray
//
// povpreview.cc
//
// Vincent LE PRINCE <vincentleprince@users.sourceforge.net>
// Copyright (C) 2000-2005 Vincent LE PRINCE
// This file is part of the TRUEVISION Package

//   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */ 
//*******************************************************************************************
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <signal.h>
#include "include/povpreview.h"
#include "include/dlgutils.h"
#include "include/preferences.h"
#include <sys/wait.h>
#include <zlib.h>
#include "config.h"
#include "include/scene.h"
#include "include/tvio.h"

// Predefined objects for material preview
const int predef_obj_num = 7;
const char *predef_obj_list[predef_obj_num] = { N_("Sphere"), N_("Box"), N_("Cone"),  N_("Ground"), N_("Sky sphere"), N_("Fisheye sky"), N_("Truncated sphere") };

// Debug stream
//ofstream debug( "debug.txt" );


//**************************************************************
// Constructor
//**************************************************************
MaterialPreview::MaterialPreview( app_objs *appref )
{
	app_ref = appref;
	PREF_DEF
	widget = NULL;
	preview_widget = NULL;
	image = NULL;
	current_material = NULL;
	start_button = NULL;
	progress_bar = NULL;
	pipe_console_name = NULL;
	predef_sizes = NULL;
	render_status = false;

	// Image & render options
	width = new TvWidget_int( N_("Width"), "W", NULL, app_ref,  640 );
	height = new TvWidget_int( N_("Height"), "H", NULL, app_ref, 480 );
	antialias = new TvWidget_bool( N_("Antialiasing"), "ANTI", NULL, app_ref, true );

	// Predefined scenes widgets
	predef_scene = new TvWidget_option_combo( N_("Object"), "OBJ",NULL, app_ref );
	predef_scene->set_list( predef_obj_list, predef_obj_num, pref->preview_object->value() );
	render_wall = new TvWidget_bool( N_("Render wall"), "WALL", NULL, app_ref, false );
	render_wall->set( pref->preview_wall->value() );
	render_floor = new TvWidget_bool( N_("Render floor"), "FLOOR", NULL, app_ref, true );
	render_floor->set( pref->preview_floor->value() );
	render_sky = new TvWidget_bool( N_("Render sky"), "SKY", NULL, app_ref, false );
	render_sky->set( pref->preview_sky->value() );
}


//*****************************************************
// Destructor
//*****************************************************
MaterialPreview::~MaterialPreview()
{
	if ( image != NULL ) 
		delete image;
	if ( predef_sizes != NULL ) 
		delete predef_sizes;
	delete width;
	delete height;
	delete antialias;
	delete predef_scene;
	delete render_wall;
	delete render_floor;
	delete render_sky;
}


//**************************************************************
// Show image as widget
//
// Get the preview widget
//**************************************************************
const int predef_sizes_num = 5;
const gchar *predef_sizes_lab[predef_sizes_num] = { "80x80", "140x140", "200x200", "300x300", "400x400" };

void MaterialPreview::get_widget( GtkWidget *box, bool tt, bool for_save_dialog )
{
	
	if ( ! for_save_dialog ) {
		// The preview widget
		widget = gtk_frame_new(NULL);
		gtk_box_pack_start( GTK_BOX(box), widget, FALSE, FALSE, 5 );
		preview_widget = gtk_image_new( );
		gtk_container_add( GTK_CONTAINER(widget), preview_widget );
	
		// The progress bar
		progress_bar = gtk_progress_bar_new();
		gtk_box_pack_start( GTK_BOX(box), progress_bar, FALSE, FALSE, 5 );
		gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), 0 );
		gtk_progress_set_show_text( GTK_PROGRESS(progress_bar), TRUE );
		gtk_progress_set_text_alignment( GTK_PROGRESS(progress_bar), 0.5, 0.3 );
		
		// Start button
		start_label = gtk_label_new( _("Preview") );
		start_button = gtk_button_new();
		gtk_container_add( GTK_CONTAINER(start_button), start_label );
		gtk_box_pack_start( GTK_BOX(box), start_button, FALSE, FALSE, 5 );
		gtk_signal_connect( GTK_OBJECT(start_button), "clicked", GTK_SIGNAL_FUNC(sign_matpreview_start_clicked), this );

		// Predefined sizes
		PREF_DEF
		predef_sizes = new TvWidget_option_combo( _("Size"), NULL, NULL, app_ref );
		predef_sizes->set_list( predef_sizes_lab, predef_sizes_num, pref->preview_size->value() );
		predef_sizes->get_widget( box, tt );
		antialias->set( pref->preview_antialias->value() );
	}
	
	// Control widgets
	predef_scene->get_widget( box, tt );
	render_wall->get_widget( box, tt );
	render_floor->get_widget( box, tt );
	render_sky->get_widget( box, tt );
	antialias->get_widget( box, tt );	
}


//**************************************************************
// Flush
//**************************************************************
void MaterialPreview::flush()
{
	predef_scene->flush();
	render_wall->flush();
	render_floor->flush();
	render_sky->flush();
	antialias->flush();
}

//********************************************************
// Start clicked
//
// start button callback
//********************************************************
void MaterialPreview::start_clicked()
{
	if ( pipe_console_name == NULL ) {
		// Start
		current_material->flush_material();
		flush();
		gtk_label_set_text( GTK_LABEL(start_label), _("Cancel") );
		start_preview( current_material, false, false );
		} else {
		// Stop
		modal = true;
		stop_preview( true );
		modal = false;
	}
}


//**************************************************************
// Start preview
//
// start the preview process
//**************************************************************
bool MaterialPreview::start_preview( Material *mat, bool save, bool ismodal )
{
	if ( mat  == NULL ) 
		return false;
	if ( mat->get_type() != TV_MAT_POV ) 
		return false;
	modal = ismodal;

	scene_file = get_scene_file( mat );
	if ( scene_file == NULL ) {
		app_warning( _("Cannot create temporary scene file ") );
		return false;
	}
	render_status = false;
	//debug << "\nSetting size !";
	//debug.flush();	

	// Image buffer
	if ( image != NULL ) 
		delete image;
	image = NULL;
	image_size = width->value() * height->value() * 3;
	image = new char[image_size];

	//debug << "\nLaunching preview !";
	//debug.flush();	
	
	// Execution de povray
	if ( render_preview( scene_file ) == false ) { 
		app_warning( _("Cannot execute povray !") ); 
		stop_render_preview();
		unlink( scene_file );
		delete scene_file;
		return false; 
	}

	//debug << "\nCreating threads !";
	//debug.flush();
	// Thread de lecture de l'image
	pthread_attr_t thread_attr;
	pthread_attr_init( &thread_attr );
	pthread_attr_setdetachstate( &thread_attr,  PTHREAD_CREATE_DETACHED );
	if( pthread_create( &output_thread, &thread_attr, reading_image_thread, this ) ) {
		app_warning( _("Cannot create image reading thread !") ); 
		stop_render_preview();
		unlink( scene_file ); 
		delete scene_file;
		return false; 	
	}

	// Thread de lecture de la console
	pthread_attr_t thread_attr2;
	pthread_attr_init( &thread_attr2 );
	pthread_attr_setdetachstate( &thread_attr2, modal ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED  );
	if( pthread_create( &console_thread, &thread_attr2, reading_console_thread, this ) ) {
		app_warning( _("Cannot create status reading thread !") ); 
		stop_render_preview();
		unlink( scene_file );
		delete scene_file;
		return false; 	
	}

	pthread_join( console_thread, NULL );
	return render_status;
}


//***********************************************************************
// Stop preview
//
// Stop the preview process
//**********************************************************************
void MaterialPreview::stop_preview( bool close_console )
{
	stop_render_preview();
	unlink( scene_file );
	delete scene_file;
	pthread_cancel( output_thread );
	if ( close_console ) pthread_cancel( console_thread );

	//debug << "\n===== Closing render ======";
	//debug.flush();
	if ( !modal ) gdk_threads_enter();
	//debug << "\nEntered gdk threads !";
	//debug.flush();
	if ( start_button != NULL ) 
		gtk_label_set_text( GTK_LABEL(start_label), _("Preview") );
	if ( progress_bar != NULL ) 
		gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), 0 );
	gdk_flush();
	if ( !modal ) 
		gdk_threads_leave();
	//debug << "\nLabel set !";
	//debug.flush();
}

//***********************************************************************
// Cancel preview
//
// cancel the preview process
//**********************************************************************
void MaterialPreview::cancel_preview()
{
	if ( pipe_console_name == NULL ) 
		return;
	stop_render_preview();
	pthread_cancel( output_thread );
	pthread_cancel( console_thread );
	unlink( scene_file );
	delete scene_file;
}


//**************************************************************
// Predefined scene
//
// Create a scene file for preview, from parameters
//**************************************************************
char * MaterialPreview::get_scene_file( Material *mat )
{
	char *tempfile = get_temp_filename();
	ofstream file( tempfile, ios::out );
	file.setf( ios::fixed, ios::floatfield );
	if ( ! file ) 
		return NULL;
	//debug << "\nCreating temp scene file -> " << tempfile;
	//debug.flush();
	
	// General scene settings
	PREF_DEF
	file << "\n\n// Global settings\nglobal_settings {";
	file << "\n\tassumed_gamma " << pref->gamma->value();
	file << "\n\tmax_trace_level 5"	<< "\n\t}";
	
	// Material definition
	mat->output_to_povray( file );

	// Camera - special one for sky_sphere & fisheye preview
	switch ( predef_scene->value() ) {
		// sky sphere	- special camera parameters for this one
		case 4:
			file << "\n\ncamera { perspective location <0, .6, -0.5>  angle 90.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,.8,0>}";
			break;
		
		// Fisheye camera
		case 5:
			file << "\n\ncamera { fisheye location <0, .6, 0>  angle 180.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,1,0>}";
			break;
	
		// Normal
		default:
			file << "\n\ncamera { perspective location <0.47, .6, -0.47>  angle 40.0 up <0,1.0,0> right <1.0,0,0> sky <0,1.0,0> look_at <0,.2,0>}";
			break;		
	}

	// Lights
	file << "\nlight_source { <-0.4,1.6,-0.6> color rgb<1,1,1> media_interaction }";
	file << "\n\nlight_source { <1.29,.36,-0.6> color rgb<0.94, 0.94, 0.94> shadowless }";

	// Floor and wall
	if ( render_floor->value() )
		file << "\nplane { <0,1,0>, 0 pigment{ checker color rgb <0.6, 0.6, 0.6>, color rgb <0.7, 0.7, 0.7> scale 0.3 }  }";
	if ( render_wall->value() && predef_scene->value() != 4 )
		file << "\nplane { <0,1,0>, 0 texture { pigment{ checker color rgb<.43,.58,.45>, color rgb<.29,.44,.33> }  scale 0.3  finish { ambient 0.65 } } rotate z*-90 rotate y*45  translate <-.5,0,.5>}";

	// Decorative sky sphere ( not for texture preview ! )
	if ( render_sky->value()   && predef_scene->value() != 4 ) {
		file << "\nsky_sphere {";
		file << "\n\tpigment { gradient y";
		file << "\n\tpigment_map {  \
			[ 0.010000 rgbft<0.847000,0.749000,0.847000,0.000000,0.000000> ] \
			[ 0.250000 wrinkles \
				color_map { \
				[ 0.000000 rgbft<0.850000,0.850000,0.850000,0.000000,0.000000> ] \
				[ 0.100000 rgbft<0.750000,0.750000,0.750000,0.000000,0.000000> ] \
				[ 0.500000 rgbft<0.258000,0.258000,0.435000,0.000000,0.000000> ] \
				[ 1.000000 rgbft<0.258000,0.258000,0.435000,0.000000,0.000000> ] 	}";
		file << "\n\tturbulence <0.650000,0.650000,0.650000> \
					 	omega 0.700000 translate <0.000000,0.000000,0.000000> scale <8.000000,1.250000,8.000000> rotate <0.000000,0.000000,5.000000> ]";
		file << "[ 1.000000 bozo \
				color_map { \
					[ 0.000000 rgbft<0.850000,0.850000,0.850000,0.000000,0.000000> ] \
					[ 0.100000 rgbft<0.550000,0.600000,0.650000,0.000000,0.000000> ]  \
					[ 0.500000 rgbft<0.184000,0.184000,0.309000,0.000000,0.000000> ] \
					[ 1.000000 rgbft<0.100000,0.100000,0.200000,0.000000,0.000000> ] \
				}";
			
		file << "\nturbulence <0.650000,0.650000,0.650000> \
		 	omega 0.707000 translate <0.000000,0.000000,0.000000> scale <8.000000,4.500000,8.000000> rotate <0.000000,0.000000,10.000000> ] \
				} } }";
     }


	// Preview objects
	switch ( predef_scene->value() ) {
		case 0:
			file << "\nsphere { <0,0,0>, 1\nscale <0.2,0.2,0.2>  ";
			file << "\n\tmaterial { ";
			mat->get_underscore_name(file);
			file << " } translate <0,.2,0> }";
			break;

		case 1:
			file << "\nbox { <-0.125,-0.125,0.125>, <0.125,0.125,-0.125>  ";
			file << "\n\tmaterial { ";
			mat->get_underscore_name(file);
			file << " } rotate z*-30 rotate y*10 translate <0, 0.2, -0.02> }";
			break;
	
		case 2:
			file << "\ncone { <0,-0.13,0>, 1, <0,0.17,0>, 0\n scale <0.21,0,0.21> ";
			file << "\n\tmaterial { ";
			mat->get_underscore_name(file);
			file << " } translate <-0.02,0.23,0.02> }";
			break;
		
		case 3:
			file << "\n\nplane {\n\t<0,1,0>,0.01";
			file << "\n\tmaterial { ";
			mat->get_underscore_name(file);
			file << " } }";
			break;
		
		case 4:
		case 5: {
			file << "\n\nsky_sphere{\n\t";
			PovMaterial *tex = ((PovMaterial*)mat);
			if ( tex->has_pigment() ) 
				tex->output_to_povray_pigment( file );
			else {
				file << "pigment { color 0.3 }";
				app_warning( _("Sky sphere requires a pigment in texture.") );
			}
			file << "\n}";
		}		
		break;

		case 6: {
			file << "\ndifference { sphere { <0,0,0>, 1}\nbox { <0,0,0>, <1,1,-1>}\nscale <0.2,0.2,0.2>  ";
			file << "\n\tmaterial { ";
			mat->get_underscore_name(file);
			file << " } translate <0,0.2,0>  }";
		}
		break;
	}

	file.close();
	return tempfile;
}



//**************************************************************
// Povray command
//
// create povray command for material preview
//**************************************************************
bool MaterialPreview::render_preview( char * source_file )
{
	// Predefined sizes
	if ( predef_sizes != NULL ) {
		int nsize = 80;	
		predef_sizes->flush();
		switch( predef_sizes->value() ) {
			case 0: nsize = 80; break;
			case 1: nsize = 140; break;
			case 2: nsize = 200; break;
			case 3: nsize = 300; break;		
			case 4: nsize = 400; break;
		}
		if ( width->value() != nsize ) {
			set_size( nsize, nsize );
			if ( image != NULL ) 
				delete image;
			image_size = width->value() * height->value() * 3;
			image = new char[image_size];
		}
	}

	// Pipes creation
	int res;
	pipe_console_name = get_temp_filename();
	res = mkfifo( pipe_console_name, S_IRUSR | S_IWUSR | O_NDELAY );
	pipe_output_name = get_temp_filename();
	res += mkfifo( pipe_output_name, S_IRUSR | S_IWUSR | O_NDELAY );
	if ( res != 0 ) 	
		return false;
	//debug << "\ncreated console pipe -> " << pipe_console_name;
	//debug.flush();
	//debug << "\ncreated output pipe -> " << pipe_output_name;
	//debug.flush();

	// Command
	PREF_DEF
	char *args[256];
	int arg_num = 0;
	
	// Povray command
	args[arg_num++] = pref->povcmd->value();
	
	// Don't pause at end
	char arg_p[] = "-P";
	args[arg_num++] = arg_p;
	
	// Don't continue render from previous session
	char arg_c[] = "-C";
	args[arg_num++] = arg_c;
	
	// Don't display image
	char arg_ud[] = "-UD";
	args[arg_num++] = arg_ud;

	// Set output format to raw
	char arg_fp[] = "+FP";
	args[arg_num++] = arg_fp;
	
	// Set quality level to 9
	char arg_q[] = "+Q9";
	args[arg_num++] = arg_q;
	
	// Set antialiasing
	if ( antialias->value() ) {
		char arg_a[] = "+A";
		args[arg_num++] = arg_a;
	}

	// Redirect all stream to console pipe
	char arg_ga[ strlen(pipe_console_name) + 5 ];
	sprintf( arg_ga, "+GA%s", pipe_console_name );
	args[arg_num++] = arg_ga;
	
	// Redirect image to data pipe
	char arg_o[ strlen(pipe_output_name) + 5 ];
	sprintf( arg_o, "+O%s", pipe_output_name );
	args[arg_num++] = arg_o;
	
	// Set predefined scene file
	char arg_i[ strlen(scene_file) + 5 ];
	sprintf( arg_i, "+I%s", scene_file );
	args[arg_num++] = arg_i;

	// Set height
	char arg_h[10];
	sprintf( arg_h, "+H%u", height->value() );
	args[arg_num++] = arg_h;

	// Set width
	char arg_w[10];
	sprintf( arg_w, "+W%u", width->value() );
	args[arg_num++] = arg_w;

	// Launch
	args[arg_num] = NULL;
	povray_pid = fork();
	if ( povray_pid == -1 ) 
		return false;
	if ( povray_pid == 0 ) {
		if ( execve( *args, args, app_ref->envp ) == -1 ) {
			unlink( pipe_console_name );
			unlink( pipe_output_name );
			exit(-1);
		}
		exit(-1);
	}

	//debug << "\nOpening pipes...";
	//debug.flush();
	pipe_console_id = open( pipe_console_name, O_RDONLY | O_NONBLOCK );
	if ( pipe_console_id == -1 ) 
		return false;
	pipe_output_id = open( pipe_output_name, O_RDONLY  | O_NONBLOCK );
	//debug << "\nPipes opened...";
	//debug.flush();
	return true;
}


//*************************************************
// Stop render preview
//*************************************************
void MaterialPreview::stop_render_preview()
{
	kill( povray_pid, 1 );
	close( pipe_output_id );
	close( pipe_console_id );
	unlink( pipe_output_name );
	unlink( pipe_console_name );
	waitpid( povray_pid, NULL, 0 );
	unlink( pipe_output_name );
	unlink( pipe_console_name );
	delete pipe_output_name;
	delete pipe_console_name; pipe_console_name = NULL;
}


//*************************************************
// Get pipe line
//
// get a line from a named pipe
//*************************************************
bool MaterialPreview::get_pipe_line( int file, char *buffer, int size )
{
//debug << "\nStart reading from pipe !"; debug.flush();
	char ch;
	int i = 0;
	while ( i < size ) {
		int res = read( file, &ch, 1 );
		if ( res == 0 ) 
			continue;
		if ( res == -1 && errno == EAGAIN )
			continue;
		if ( res == -1 ) 
			return false;
		if ( ch == '\n' || ch == '\r' ) 
			break;
		buffer[i++] = ch;
	}
	buffer[i] = '\0';
	//debug << "\nRead one line from pipe !"; debug.flush();
	return true;
}




//*******************************************************
// Read console
//
// Read the console thread
//*******************************************************
void MaterialPreview::read_console()
{
	pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
	pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );

	//debug << "\nStarted console reading thread !!";
	//debug.flush();
	char console_line[255] = "\0";
	bool finished = false;
	if ( get_pipe_line( pipe_console_id, console_line, 254 ) == false || !strstr( console_line, "Options" ) ) {
		//debug << "\nError : read_console init planting !";
		//debug << "\nline ->" << console_line << "<-";
		//debug.flush();
		stop_preview( false );
		if ( ! modal ) 
			gdk_threads_enter();
		app_warning( _("Error while executing povray ! ( init error )") );
		gdk_flush();
		if ( !modal ) 
			gdk_threads_leave();
		pthread_exit(NULL);
	}

	// lecture de la console
	int console_line_offset = 0;
	int res;
	char ch;
	while ( finished == false ) {
		pthread_testcancel();
		console_line[console_line_offset] = '\0';
		res = read( pipe_console_id, &ch, 1 );
		if ( res == -1 || res == 0 ) 
			continue;
		console_line[console_line_offset++] = ch;
		pthread_testcancel();
		if ( console_line_offset == 254 || ch == '\n' ) {
			// Analyse et stockage ( gestion des erreurs )
			if ( strstr( console_line, "Total Time" ) != NULL ) 
				finished = true;

			bool error = false;
			if ( strstr( console_line, "Error" ) != NULL ) 
				error = true;
			if ( strstr( console_line, "error" ) != NULL ) 
				error = true;
			//if ( strstr( console_line, "Possible" ) != NULL ) 
			//	error = false;			
			if ( error ) {
				if ( !modal ) 
					gdk_threads_enter();
				app_warning( _("Povray reports an error, possible artifacts or truevision bug.") );
				gdk_flush();
				if ( !modal ) 
					gdk_threads_leave();
				stop_preview( modal );
				pthread_exit(NULL);
			}
		console_line_offset = 0;
		}
	}
	render_status = true;
	while (  render_finished == false ) 
		sleep(1);
	stop_preview( false );
	//debug << "\nLeaving console thread !! ";
	//debug.flush();
	pthread_exit(NULL);
}




//*******************************************************
// Read utput
//
// Image data thread read process
//*******************************************************
void MaterialPreview::read_output()
{
	//debug << "\nStarted output reading thread !!";
	//debug.flush();

	pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL );
	pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, NULL );
	render_finished = false;

	// Flow control
	int largeur = width->value();
	int hauteur = height->value();
	guchar output_line[largeur*3];
	int output_line_num = 0;
	int output_line_offset = 0;
	int header = 0;
	char ch;
	int res;
	bool finished = false;
	GdkRectangle rect;
	rect.width = largeur;
	rect.height = 1;
	rect.x= 0;
	while ( finished == false ) {
		pthread_testcancel();
		res = read( pipe_output_id, &ch, 1 );
		//debug << "\nres -> " << res;
		if ( res == -1 || res == 0 ) 
			continue;
		if ( header < 3 ) {
			if ( ch == '\n' ) 
				header++;
			continue; 
		}
		output_line[output_line_offset++] = ch;
		if ( output_line_offset == largeur*3 ) {
			//debug << "\none line completed ";
			//debug.flush();
			if ( !modal ) 
				gdk_threads_enter();
		
			if ( image != NULL ) 
				memcpy( image + (largeur-1) * output_line_num * 3, output_line, (largeur-1)*3 );
			GdkPixbuf *buffer = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8, largeur, hauteur, (largeur-1)*3, NULL, NULL );
			gtk_image_set_from_pixbuf( GTK_IMAGE(preview_widget), buffer );
			g_object_unref( buffer );
		
			if ( progress_bar != NULL ) 
				gtk_progress_bar_update( GTK_PROGRESS_BAR(progress_bar), (float)output_line_num / (float)hauteur );
			//debug << "\nProgress -> " << (float)output_line_num / (float)height->value();
			//debug.flush();
			rect.y = output_line_num++;
			gdk_flush();
			if ( !modal ) 
				gdk_threads_leave();
			output_line_offset = 0;
			if ( output_line_num == hauteur -1 ) 
				finished = true;
		}
	}
	//debug << "\nLeaving output thread !!";
	//debug.flush();
	render_finished = true;
	pthread_exit(NULL);
}


//********************************************************
// Save as preview
//
// save image for thumnails
//********************************************************
void MaterialPreview::save_as_preview( ofstream & file )
{
	if ( image == NULL ) 
		return;

	// compression
	char *zimage = new char[image_size+30];
	long int zimage_size = image_size+30;
	compress2( (Bytef*)zimage, (uLongf*)&zimage_size, (Bytef*)image, (uLong)image_size, 9 ); 
	delete image;
	image = NULL;

	// sauvegarde
	file << "\nPREVIEW{ SIZE=" << zimage_size << " DATA=";
	for( int i = 0 ; i < zimage_size ; i++ )
		file << zimage[i];
	file << " }\n";

	delete zimage;
}



//********************************************************
// Save  preview settings
//
// save preview parameters in preferences
//********************************************************
void MaterialPreview::save_preview_settings()
{
	PREF_DEF
	if ( ! pref->save_preview_settings->value() ) 
		return;
	predef_sizes->flush();
	predef_scene->flush();
	render_wall->flush();
	render_floor->flush();
	render_sky->flush();
	antialias->flush();
	pref->preview_size->set( predef_sizes->value() );
	pref->preview_object->set( predef_scene->value() );
	pref->preview_wall->set( render_wall->value() );
	pref->preview_floor->set( render_floor->value() );
	pref->preview_sky->set( render_sky->value() );
	pref->preview_antialias->set( antialias->value() );
}
