/* This file contains code for unified image loading from many file formats */
/********************************************************************/
/* Copyright (c) 2001,2004 Sasha Vasko <sasha at aftercode.net>     */
/* Copyright (c) 2004 Maxim Nikulin <nikulin at gorodok.net>        */
/********************************************************************/
/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/*#undef NO_DEBUG_OUTPUT*/
#undef LOCAL_DEBUG
#undef DO_CLOCKING
#undef DEBUG_TRANSP_GIF

#ifdef _WIN32
#include "win32/config.h"
#else
#include "config.h"
#endif

#ifdef HAVE_PNG
/* Include file for users of png library. */
# ifdef HAVE_BUILTIN_PNG
#  include "libpng/png.h"
# else
#  include <png.h>
# endif
#else
#include <setjmp.h>
#endif
#ifdef HAVE_JPEG
/* Include file for users of png library. */
# undef HAVE_STDLIB_H
# ifndef X_DISPLAY_MISSING
#  include <X11/Xmd.h>
# endif
# ifdef HAVE_BUILTIN_JPEG
#  include "libjpeg/jpeglib.h"
# else
#  include <jpeglib.h>
# endif
#endif

#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <stdlib.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#endif
#include <string.h>
#include <ctype.h>
/* <setjmp.h> is used for the optional error recovery mechanism */

#ifdef const
#undef const
#endif
#ifdef _WIN32
# include "win32/afterbase.h"
#else
# include "afterbase.h"
#endif
#ifdef HAVE_GIF
# ifdef HAVE_BUILTIN_UNGIF
#  include "libungif/gif_lib.h"
# else
#  include <gif_lib.h>
# endif
#endif
#ifdef HAVE_TIFF
#include <tiff.h>
#include <tiffio.h>
#endif
#ifdef HAVE_LIBXPM
#ifdef HAVE_LIBXPM_X11
#include <X11/xpm.h>
#else
#include <xpm.h>
#endif
#endif

#include "asimage.h"
#include "imencdec.h"
#include "bmp.h"
#include "ximage.h"
#include "xcf.h"
#include "xpm.h"
#include "ungif.h"
#include "import.h"
#include "asimagexml.h"
#include "transform.h"

#ifdef jmpbuf
#undef jmpbuf
#endif


/***********************************************************************************/
/* High level interface : 														   */
static char *locate_image_file( const char *file, char **paths );
static ASImageFileTypes	check_image_type( const char *realfilename );

as_image_loader_func as_image_file_loaders[ASIT_Unknown] =
{
	xpm2ASImage ,
	xpm2ASImage ,
	xpm2ASImage ,
	png2ASImage ,
	jpeg2ASImage,
	xcf2ASImage ,
	ppm2ASImage ,
	ppm2ASImage ,
	bmp2ASImage ,
	ico2ASImage ,
	ico2ASImage ,
	gif2ASImage ,
	tiff2ASImage,
	xml2ASImage ,
	NULL,
	tga2ASImage,
	NULL,
	NULL,
	NULL
};

const char *as_image_file_type_names[ASIT_Unknown+1] =
{
	"XPM" ,
	"Z-compressed XPM" ,
	"GZ-compressed XPM" ,
	"PNG" ,
	"JPEG",
	"GIMP Xcf" ,
	"PPM" ,
	"PNM" ,
	"MS Windows Bitmap" ,
	"MS Windows Icon" ,
	"MS Windows Cursor" ,
	"GIF" ,
	"TIFF",
	"AfterStep XML" ,
	"XBM",
	"Targa",
	"PCX",
	"HTML",
	"XML",
	"Unknown"
};

 
ASImage *
file2ASImage_extra( const char *file, ASImageImportParams *iparams )
{
	int 		  filename_len ;
	int 		  subimage = -1 ;
	char 		 *realfilename = NULL, *tmp = NULL ;
	register int i;

	ASImage *im = NULL;
	char *g_var ;
	ASImageImportParams dummy_iparams = {0};

	if( iparams == NULL )
		iparams = &dummy_iparams ;

	if( (g_var = getenv( "SCREEN_GAMMA" )) != NULL )
		iparams->gamma = atof(g_var);

	if( file )
	{
		filename_len = strlen(file);
#ifdef _WIN32
		{		
			int i = 0 ; 
			while( iparams->search_path[i] != NULL ) 
				unix_path2dos_path( iparams->search_path[i] );
		}
#endif

		/* first lets try to find file as it is */
		if( (realfilename = locate_image_file(file, iparams->search_path)) == NULL )
		{
			tmp = safemalloc( filename_len+3+1);
			strcpy(tmp, file);
		}
		if( realfilename == NULL )
		{ /* let's try and see if appending .gz will make any difference */
			strcpy(&(tmp[filename_len]), ".gz");
			realfilename = locate_image_file(tmp,iparams->search_path);
		}
		if( realfilename == NULL )
		{ /* let's try and see if appending .Z will make any difference */
			strcpy(&(tmp[filename_len]), ".Z");
			realfilename = locate_image_file(tmp,iparams->search_path);
		}
		if( realfilename == NULL )
		{ /* let's try and see if we have subimage number appended */
			for( i = filename_len-1 ; i > 0; i-- )
				if( !isdigit( (int)tmp[i] ) )
					break;
			if( i < filename_len-1 && i > 0 )
				if( tmp[i] == '.' )                 /* we have possible subimage number */
				{
					subimage = atoi( &tmp[i+1] );
					tmp[i] = '\0';
					filename_len = i ;
					realfilename = locate_image_file(tmp,iparams->search_path);
					if( realfilename == NULL )
					{ /* let's try and see if appending .gz will make any difference */
						strcpy(&(tmp[filename_len]), ".gz");
						realfilename = locate_image_file(tmp,iparams->search_path);
					}
					if( realfilename == NULL )
					{ /* let's try and see if appending .Z will make any difference */
						strcpy(&(tmp[filename_len]), ".Z");
						realfilename = locate_image_file(tmp,iparams->search_path);
					}
				}
		}
		if( tmp != realfilename && tmp != NULL )
			free( tmp );
	}
	if( realfilename )
	{
		ASImageFileTypes file_type = check_image_type( realfilename );
		if( file_type == ASIT_Unknown )
			show_error( "Hmm, I don't seem to know anything about format of the image file \"%s\"\n.\tPlease check the manual", realfilename );
		else if( as_image_file_loaders[file_type] )
			im = as_image_file_loaders[file_type](realfilename, iparams);
		else
			show_error( "Support for the format of image file \"%s\" has not been implemented yet.", realfilename );
#ifndef NO_DEBUG_OUTPUT
		show_progress( "image loaded from \"%s\"", realfilename );
#endif
		if( realfilename != file )
			free( realfilename );
	}else
		show_error( "I'm terribly sorry, but image file \"%s\" is nowhere to be found.", file );

	return im;
}

ASImage *
file2ASImage( const char *file, ASFlagType what, double gamma, unsigned int compression, ... )
{
	int i ;
	char 		 *paths[MAX_SEARCH_PATHS+1] ;
	ASImageImportParams iparams ;
	va_list       ap;

	iparams.flags = 0 ;
	iparams.width = 0 ;
	iparams.height = 0 ;
	iparams.filter = SCL_DO_ALL ;
	iparams.gamma = gamma ;
	iparams.gamma_table = NULL ;
	iparams.compression = compression ;
	iparams.format = ASA_ASImage ;
	iparams.search_path = &(paths[0]);
	iparams.subimage = 0 ;

	va_start (ap, compression);
	for( i = 0 ; i < MAX_SEARCH_PATHS ; i++ )
	{	
		if( (paths[i] = va_arg(ap,char*)) == NULL )
			break;
	}		   
	paths[MAX_SEARCH_PATHS] = NULL ;
	va_end (ap);

	return file2ASImage_extra( file, &iparams );

}

Pixmap
file2pixmap(ASVisual *asv, Window root, const char *realfilename, Pixmap *mask_out)
{
	Pixmap trg = None;
#ifndef X_DISPLAY_MISSING
	Pixmap mask = None ;
	if( asv && realfilename )
	{
		double gamma = SCREEN_GAMMA;
		char  *gamma_str;
		ASImage *im = NULL;

		if ((gamma_str = getenv ("SCREEN_GAMMA")) != NULL)
		{
			gamma = atof (gamma_str);
			if (gamma == 0.0)
				gamma = SCREEN_GAMMA;
		}

		im = file2ASImage( realfilename, 0xFFFFFFFF, gamma, 0, NULL );

		if( im != NULL )
		{
			trg = asimage2pixmap( asv, root, im, NULL, False );
			if( mask_out )
				if( get_flags( get_asimage_chanmask(im), SCL_DO_ALPHA ) )
					mask = asimage2mask( asv, root, im, NULL, False );
			destroy_asimage( &im );
		}
	}
	if( mask_out )
	{
		if( *mask_out && asv )
			XFreePixmap( asv->dpy, *mask_out );
		*mask_out = mask ;
	}
#endif
	return trg ;
}

static ASImage *
load_image_from_path( const char *file, char **path, double gamma)
{
	if( path[2] == NULL )
		return file2ASImage( file, 0xFFFFFFFF, gamma, 100, path[0], path[1], NULL );
#if (MAX_SEARCH_PATHS!=8)
#error "Fixme: please update file2ASImage call to match max number of search paths in ImageManager"
#else
	else
		return file2ASImage( file, 0xFFFFFFFF, gamma, 100, path[0], path[1], path[2],
			    	       path[3], path[4], path[5], path[6], path[7], NULL );
#endif

	return NULL ;
}

ASImage *
get_asimage( ASImageManager* imageman, const char *file, ASFlagType what, unsigned int compression )
{
	ASImage *im = NULL ;
	if( imageman && file )
		if( (im = fetch_asimage(imageman, file )) == NULL )
		{
			im = load_image_from_path( file, &(imageman->search_path[0]), imageman->gamma);
			if( im )
			{
				store_asimage( imageman, im, file );
				set_flags( im->flags, ASIM_NAME_IS_FILENAME );
			}
				
		}
	return im;
}

Bool
reload_asimage_manager( ASImageManager *imman )
{
#if (HAVE_AFTERBASE_FLAG==1)
	if( imman != NULL ) 
	{
		ASHashIterator iter ;
		if( start_hash_iteration (imman->image_hash, &iter) )
		{
			do
			{
				ASImage *im = curr_hash_data( &iter );
/*fprintf( stderr, "im = %p. flags = 0x%lX\n", im, im->flags );		*/
				if( get_flags( im->flags, ASIM_NAME_IS_FILENAME ) )
				{
/*fprintf( stderr, "reloading image \"%s\" ...", im->name );*/
					ASImage *reloaded_im = load_image_from_path( im->name, &(imman->search_path[0]), imman->gamma);
/*fprintf( stderr, "Done. reloaded_im = %p.\n", reloaded_im );*/					
					if( reloaded_im ) 
					{
						if( asimage_replace (im, reloaded_im) ) 
							free( reloaded_im );
						else
							destroy_asimage( &reloaded_im );
					}				
				}
			}while( next_hash_item( &iter ) );
			return True;		
		}
	}
#endif
	return False;
}


ASImageListEntry * 
ref_asimage_list_entry( ASImageListEntry *entry )
{
	if( entry ) 
	{
		if( IS_ASIMAGE_LIST_ENTRY(entry) )
			++(entry->ref_count);
		else
			entry = NULL ; 
	}
	return entry;
}
	 
ASImageListEntry *
unref_asimage_list_entry( ASImageListEntry *entry )
{
	if( entry ) 
	{	
		if( IS_ASIMAGE_LIST_ENTRY(entry) )
		{
			--(entry->ref_count);
			if( entry->ref_count  <= 0 )
			{
				ASImageListEntry *prev = entry->prev ; 
				ASImageListEntry *next = entry->next ; 
				if( !IS_ASIMAGE_LIST_ENTRY(prev) )
					prev = NULL ; 
				if( !IS_ASIMAGE_LIST_ENTRY(next) )
					next = NULL ; 
				if( prev ) 
					prev->next = next ; 
				if( next ) 
					next->prev = prev ; 

				if( entry->preview ) 
					safe_asimage_destroy( entry->preview );
				if( entry->name )
					free( entry->name );
				if( entry->fullfilename )
					free( entry->fullfilename );
				if( entry->buffer ) 
					destroy_asimage_list_entry_buffer( &(entry->buffer) );
				memset( entry, 0x00, sizeof(ASImageListEntry));
				free( entry );
				entry = NULL ; 
			}	 
		}else
			entry = NULL ;
	}
	return entry;
}	 

ASImageListEntry *
create_asimage_list_entry()
{
	ASImageListEntry *entry = safecalloc( 1, sizeof(ASImageListEntry));
	entry->ref_count = 1 ; 
	entry->magic = MAGIC_ASIMAGE_LIST_ENTRY ; 
	return entry;
}

void
destroy_asimage_list( ASImageListEntry **plist )
{
	if( plist )
	{		   
		ASImageListEntry *curr = *plist ;
		while( IS_ASIMAGE_LIST_ENTRY(curr) )
		{	
			ASImageListEntry *to_delete = curr ; 
			curr = curr->next ;
		 	unref_asimage_list_entry( to_delete );
		}
		*plist = NULL ;
	}
}

void destroy_asimage_list_entry_buffer( ASImageListEntryBuffer **pbuffer )
{
	if( pbuffer && *pbuffer ) 
	{		 
		if( (*pbuffer)->data ) 
			free( (*pbuffer)->data ) ;
		free( *pbuffer );
		*pbuffer = NULL ;
	}
}	 

struct ASImageListAuxData
{
	ASImageListEntry **pcurr;
	ASImageListEntry *last ;
	ASFlagType preview_type ;
	unsigned int preview_width ;
	unsigned int preview_height ;
	unsigned int preview_compression ;
	ASVisual *asv;
};

Bool 
direntry2ASImageListEntry( const char *fname, const char *fullname, 
						   struct stat *stat_info, void *aux_data)
{
	struct ASImageListAuxData *data = (struct ASImageListAuxData*)aux_data;
	ASImageFileTypes file_type ;
	ASImageListEntry *curr ;
	   	
	if (S_ISDIR (stat_info->st_mode))
		return False;
	
	file_type = check_image_type( fullname );
	if( file_type != ASIT_Unknown && as_image_file_loaders[file_type] == NULL )
		file_type = ASIT_Unknown ;

	curr = create_asimage_list_entry();
	*(data->pcurr) = curr ; 
	if( data->last )
		data->last->next = curr ;
	curr->prev = data->last ;
	data->last = curr ;
	data->pcurr = &(data->last->next);

	curr->name = mystrdup( fname );
	curr->fullfilename = mystrdup(fullname);
	curr->type = file_type ;
   	curr->d_mode = stat_info->st_mode;
	curr->d_mtime = stat_info->st_mtime;
	curr->d_size  = stat_info->st_size;

	if( curr->type != ASIT_Unknown && data->preview_type != 0 )
	{
		ASImageImportParams iparams = {0} ;
		ASImage *im = as_image_file_loaders[file_type](fullname, &iparams);
		if( im )
		{
			int scale_width = im->width ;
			int scale_height = im->height ;
			int tile_width = im->width ;
			int tile_height = im->height ;

			if( data->preview_width > 0 )
			{
				if( get_flags( data->preview_type, SCALE_PREVIEW_H ) )
					scale_width = data->preview_width ;
				else
					tile_width = data->preview_width ;
			}
			if( data->preview_height > 0 )
			{
				if( get_flags( data->preview_type, SCALE_PREVIEW_V ) )
					scale_height = data->preview_height ;
				else
					tile_height = data->preview_height ;
			}
			if( scale_width != im->width || scale_height != im->height )
			{
				ASImage *tmp = scale_asimage( data->asv, im, scale_width, scale_height, ASA_ASImage, data->preview_compression, ASIMAGE_QUALITY_DEFAULT );
				if( tmp != NULL )
				{
					destroy_asimage( &im );
					im = tmp ;
				}
			}
			if( tile_width != im->width || tile_height != im->height )
			{
				ASImage *tmp = tile_asimage( data->asv, im, 0, 0, tile_width, tile_height, TINT_NONE, ASA_ASImage, data->preview_compression, ASIMAGE_QUALITY_DEFAULT );
				if( tmp != NULL )
				{
					destroy_asimage( &im );
					im = tmp ;
				}
			}
		}

		curr->preview = im ;
	}
	return True;
}


ASImageListEntry *
get_asimage_list( ASVisual *asv, const char *dir,
	              ASFlagType preview_type, double gamma,
				  unsigned int preview_width, unsigned int preview_height,
				  unsigned int preview_compression,
				  unsigned int *count_ret,
				  int (*select) (const char *) )
{
	ASImageListEntry *im_list = NULL ;
#ifndef _WIN32
	struct ASImageListAuxData aux_data ; 
	int count ; 
	
	aux_data.pcurr = &im_list;
	aux_data.last = NULL;
	aux_data.preview_type = preview_type;
	aux_data.preview_width = preview_width;
	aux_data.preview_height = preview_height;
	aux_data.preview_compression  = preview_compression;
	aux_data.asv = asv ; 
	
	
	if( asv == NULL || dir == NULL )
		return NULL ;

	count = my_scandir_ext ((char*)dir, select, direntry2ASImageListEntry, &aux_data);

	if( count_ret )
		*count_ret = count ;
#endif
	return im_list;
}

char *format_asimage_list_entry_details( ASImageListEntry *entry, Bool vertical )
{
	char *details_text ;

	if( entry ) 
	{	
		int type = (entry->type>ASIT_Unknown)?ASIT_Unknown:entry->type ; 
		details_text = safemalloc(128);
		if( entry->preview ) 
			sprintf( details_text, vertical?"File type: %s\nSize %dx%d":"File type: %s; Size %dx%d", as_image_file_type_names[type], entry->preview->width, entry->preview->height ); 	  
		else 
			sprintf( details_text, "File type: %s", as_image_file_type_names[type]);
	}else
		details_text = mystrdup("");		   
	return details_text;
}	 

Bool 
load_asimage_list_entry_data( ASImageListEntry *entry, size_t max_bytes )
{
	char * new_buffer ; 
	size_t new_buffer_size ;
	FILE *fp;
	Bool binary = False ; 
	if( entry == NULL ) 
		return False;
	if( entry->buffer == NULL ) 
		entry->buffer = safecalloc( 1, sizeof(ASImageListEntryBuffer) );
	if( entry->buffer->size == entry->d_size || entry->buffer->size >= max_bytes )
		return True;
	new_buffer_size = min( max_bytes, (size_t)entry->d_size ); 
	new_buffer = malloc( new_buffer_size );
	if( new_buffer == NULL ) 
		return False ;
	if( entry->buffer->size > 0 ) 
	{	
		memcpy( new_buffer, entry->buffer->data, entry->buffer->size ) ;
		free( entry->buffer->data );
	}
	entry->buffer->data = new_buffer ; 
	/* TODO read new_buffer_size - entry->buffer_size bytes into the end of the buffer */
	fp = fopen(entry->fullfilename, "rb");
	if ( fp != NULL ) 
	{
		int len = new_buffer_size - entry->buffer->size ;
		if( entry->buffer->size > 0 ) 
			fseek( fp, entry->buffer->size, SEEK_SET );
		len = fread(entry->buffer->data, 1, len, fp);
		if( len > 0 ) 
			entry->buffer->size += len ;
		fclose(fp);
	}

	if( entry->type == ASIT_Unknown ) 
	{
		int i = entry->buffer->size ; 
		register char *ptr = entry->buffer->data ;
		while ( --i >= 0 )	
			if( !isprint(ptr[i]) && ptr[i] != '\n'&& ptr[i] != '\r'&& ptr[i] != '\t' )	
				break;
		binary = (i >= 0);				
	}else
		binary = (entry->type != ASIT_Xpm  && entry->type != ASIT_XMLScript &&
			  	  entry->type != ASIT_HTML && entry->type != ASIT_XML ); 
	if( binary ) 
		set_flags( entry->buffer->flags, ASILEB_Binary );
   	else
		clear_flags( entry->buffer->flags, ASILEB_Binary );
	 


	return True;
}

/***********************************************************************************/
/* Some helper functions :                                                         */

static char *
locate_image_file( const char *file, char **paths )
{
	char *realfilename = NULL;
	if( file != NULL )
	{
		realfilename = mystrdup( file );
#ifdef _WIN32
		unix_path2dos_path( realfilename );
#endif
		
		if( CheckFile( realfilename ) != 0 )
		{
			free( realfilename ) ;
			realfilename = NULL ;
			if( paths != NULL )
			{	/* now lets try and find the file in any of the optional paths :*/
				register int i = 0;
				do
				{
					if( i > 0 ) 
					{	
						show_progress( "looking for image \"%s\" in path [%s]", file, paths[i] );
					}		
					realfilename = find_file( file, paths[i], R_OK );
				}while( realfilename == NULL && paths[i++] != NULL );
			}
		}
	}
	return realfilename;
}

static FILE*
open_image_file( const char *path )
{
	FILE *fp = NULL;
	if ( path )
	{
		if ((fp = fopen (path, "rb")) == NULL)
			show_error("cannot open image file \"%s\" for reading. Please check permissions.", path);
	}else
		fp = stdin ;
	return fp ;
}

static ASImageFileTypes
check_image_type( const char *realfilename )
{
	int filename_len = strlen( realfilename );
#define FILE_HEADER_SIZE	512
	char head[FILE_HEADER_SIZE+1] ;
	int bytes_in = 0 ;
	FILE *fp ;
	ASImageFileTypes type = ASIT_Unknown ;
	/* lets check if we have compressed xpm file : */
	if( filename_len > 5 && (mystrncasecmp( realfilename+filename_len-5, ".html", 5 ) == 0 || 
							 mystrncasecmp( realfilename+filename_len-4, ".htm", 4 ) == 0 ))
		type = ASIT_HTML;
	else if( filename_len > 7 && mystrncasecmp( realfilename+filename_len-7, ".xpm.gz", 7 ) == 0 )
		type = ASIT_GZCompressedXpm;
	else if( filename_len > 6 && mystrncasecmp( realfilename+filename_len-6, ".xpm.Z", 6 ) == 0 )
		type = ASIT_ZCompressedXpm ;
	else if( (fp = open_image_file( realfilename )) != NULL )
	{
		bytes_in = fread( &(head[0]), sizeof(char), FILE_HEADER_SIZE, fp );
		head[FILE_HEADER_SIZE] = '\0' ;
		DEBUG_OUT("%s: head[0]=0x%2.2X(%d),head[2]=0x%2.2X(%d)\n", realfilename+filename_len-4, head[0], head[0], head[2], head[2] );
/*		fprintf( stderr, " IMAGE FILE HEADER READS : [%s][%c%c%c%c%c%c%c%c][%s], bytes_in = %d\n", (char*)&(head[0]),
						head[0], head[1], head[2], head[3], head[4], head[5], head[6], head[7], strstr ((char *)&(head[0]), "XPM"),bytes_in );
 */
		if( bytes_in > 3 )
		{
			if( (CARD8)head[0] == 0xff && (CARD8)head[1] == 0xd8 && (CARD8)head[2] == 0xff)
				type = ASIT_Jpeg;
			else if (strstr ((char *)&(head[0]), "XPM") != NULL)
				type =  ASIT_Xpm;
			else if (head[1] == 'P' && head[2] == 'N' && head[3] == 'G')
				type = ASIT_Png;
			else if (head[0] == 'G' && head[1] == 'I' && head[2] == 'F')
				type = ASIT_Gif;
			else if (head[0] == head[1] && (head[0] == 'I' || head[0] == 'M'))
				type = ASIT_Tiff;
			else if (head[0] == 'P' && isdigit(head[1]))
				type = (head[1]!='5' && head[1]!='6')?ASIT_Pnm:ASIT_Ppm;
			else if (head[0] == 0xa && head[1] <= 5 && head[2] == 1)
				type = ASIT_Pcx;
			else if (head[0] == 'B' && head[1] == 'M')
				type = ASIT_Bmp;
			else if (head[0] == 0 && head[2] == 1 && mystrncasecmp(realfilename+filename_len-4, ".ICO", 4)==0 )
				type = ASIT_Ico;
			else if (head[0] == 0 && head[2] == 2 &&
						(mystrncasecmp(realfilename+filename_len-4, ".CUR", 4)==0 ||
						 mystrncasecmp(realfilename+filename_len-4, ".ICO", 4)==0) )
				type = ASIT_Cur;
		}
		if( type == ASIT_Unknown && bytes_in  > 6 )
		{
			if( mystrncasecmp( head, "<HTML>", 6 ) == 0 )
				type = ASIT_HTML;	
		}	 
		if( type == ASIT_Unknown && bytes_in  > 8 )
		{
			if( strncmp(&(head[0]), XCF_SIGNATURE, (size_t) XCF_SIGNATURE_LEN) == 0)
				type = ASIT_Xcf;
	   		else if (head[0] == 0 && head[1] == 0 &&
			    	 head[2] == 2 && head[3] == 0 && head[4] == 0 && head[5] == 0 && head[6] == 0 && head[7] == 0)
				type = ASIT_Targa;
			else if (strncmp (&(head[0]), "#define", (size_t) 7) == 0)
				type = ASIT_Xbm;
			else
			{/* the nastiest check - for XML files : */
				int i ;

				type = ASIT_XMLScript ;
				for( i = 0 ; i < bytes_in ; ++i ) if( !isspace(head[i]) ) break;
				while( bytes_in > 0 && type == ASIT_XMLScript )
				{
					if( i >= bytes_in )
					{	
						bytes_in = fread( &(head[0]), sizeof(CARD8), FILE_HEADER_SIZE, fp );
						for( i = 0 ; i < bytes_in ; ++i ) if( !isspace(head[i]) ) break;
					}
					else if( head[i] != '<' )
						type = ASIT_Unknown ;
					else if( mystrncasecmp( &(head[i]), "<!DOCTYPE ", 10 ) == 0 ) 
					{	
						type = ASIT_XML ;
						for( i += 9 ; i < bytes_in ; ++i ) if( !isspace(head[i]) ) break;
						if( i < bytes_in ) 
						{
					 		if( mystrncasecmp( &(head[i]), "afterstep-image-xml", 19 ) == 0 ) 			
							{
								i += 19 ;	  
								type = ASIT_XMLScript ;
							}
						}	 
					}else
					{
						while( bytes_in > 0 && type == ASIT_XMLScript )
						{
							while( ++i < bytes_in )
								if( !isspace(head[i]) )
								{
									if( !isprint(head[i]) )
									{
										type = ASIT_Unknown ;
										break ;
									}else if( head[i] == '>' )
										break ;
								}

							if( i >= bytes_in )
							{	
								bytes_in = fread( &(head[0]), sizeof(CARD8), FILE_HEADER_SIZE, fp );
								i = 0 ; 
							}else
								break ;
						}
						break;
					}	
				}
			}
		}
		fclose( fp );
	}
	return type;
}


ASImageFileTypes
check_asimage_file_type( const char *realfilename )
{
	if( realfilename == NULL ) 
		return ASIT_Unknown;
	return check_image_type( realfilename );
}

/***********************************************************************************/
#ifdef HAVE_XPM      /* XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM */

#ifdef LOCAL_DEBUG
Bool print_component( CARD32*, int, unsigned int );
#endif

static ASImage *
xpm_file2ASImage( ASXpmFile *xpm_file, unsigned int compression )
{
	ASImage *im = NULL ;
	int line = 0;

	LOCAL_DEBUG_OUT( "do_alpha is %d. im->height = %d, im->width = %d", xpm_file->do_alpha, xpm_file->height, xpm_file->width );
	if( build_xpm_colormap( xpm_file ) )
		if( (im = create_xpm_image( xpm_file, compression )) != NULL )
		{
			int bytes_count = im->width*4 ;
			ASFlagType rgb_flags = ASStorage_RLEDiffCompress|ASStorage_32Bit ;
			ASFlagType alpha_flags = ASStorage_RLEDiffCompress|ASStorage_32Bit ;
			if( !xpm_file->full_alpha ) 
				alpha_flags |= ASStorage_Bitmap ;
			for( line = 0 ; line < xpm_file->height ; ++line )
			{
				if( !convert_xpm_scanline( xpm_file, line ) )
					break;
				im->channels[IC_RED][line]   = store_data( NULL, (CARD8*)xpm_file->scl.red, bytes_count, rgb_flags, 0);
				im->channels[IC_GREEN][line] = store_data( NULL, (CARD8*)xpm_file->scl.green, bytes_count, rgb_flags, 0);
				im->channels[IC_BLUE][line]  = store_data( NULL, (CARD8*)xpm_file->scl.blue, bytes_count, rgb_flags, 0);
				if( xpm_file->do_alpha )
					im->channels[IC_ALPHA][line]  = store_data( NULL, (CARD8*)xpm_file->scl.alpha, bytes_count, alpha_flags, 0);
#ifdef LOCAL_DEBUG
				printf( "%d: \"%s\"\n",  line, xpm_file->str_buf );
				print_component( xpm_file->scl.red, 0, xpm_file->width );
				print_component( xpm_file->scl.green, 0, xpm_file->width );
				print_component( xpm_file->scl.blue, 0, xpm_file->width );
#endif
			}
		}
	return im ;
}

ASImage *
xpm2ASImage( const char * path, ASImageImportParams *params )
{
	ASXpmFile *xpm_file = NULL;
	ASImage *im = NULL ;
	START_TIME(started);

	LOCAL_DEBUG_CALLER_OUT ("(\"%s\", 0x%lX)", path, params->flags);
	if( (xpm_file=open_xpm_file(path)) == NULL )
	{
		show_error("cannot open image file \"%s\" for reading. Please check permissions.", path);
		return NULL;
	}

	im = xpm_file2ASImage( xpm_file, params->compression );
	close_xpm_file( &xpm_file );

	SHOW_TIME("image loading",started);
	return im;
}

ASXpmFile *open_xpm_data(const char **data);

ASImage *
xpm_data2ASImage( const char **data, ASImageImportParams *params )
{
	ASXpmFile *xpm_file = NULL;
	ASImage *im = NULL ;
	START_TIME(started);

    LOCAL_DEBUG_CALLER_OUT ("(\"%s\", 0x%lX)", (char*)data, params->flags);
	if( (xpm_file=open_xpm_data(data)) == NULL )
	{
		show_error("cannot read XPM data.");
		return NULL;
	}

	im = xpm_file2ASImage( xpm_file, params->compression );
	close_xpm_file( &xpm_file );

	SHOW_TIME("image loading",started);
	return im;
}

#else  			/* XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM */

ASImage *
xpm2ASImage( const char * path, ASImageImportParams *params )
{
	show_error( "unable to load file \"%s\" - XPM image format is not supported.\n", path );
	return NULL ;
}

#endif 			/* XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM XPM */
/***********************************************************************************/

static inline void
apply_gamma( register CARD8* raw, register CARD8 *gamma_table, unsigned int width )
{
	if( gamma_table )
	{	
		register unsigned int i ;
		for( i = 0 ; i < width ; ++i )
			raw[i] = gamma_table[raw[i]] ;
	}
}

/***********************************************************************************/
#ifdef HAVE_PNG		/* PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG */
ASImage *
png2ASImage( const char * path, ASImageImportParams *params )
{
	FILE 		 *fp ;
    double        image_gamma = DEFAULT_PNG_IMAGE_GAMMA;
	png_structp   png_ptr;
	png_infop     info_ptr;
	png_uint_32   width, height;
	int           bit_depth, color_type, interlace_type;
	int           intent;
	ASScanline    buf;
	Bool 		  do_alpha = False, grayscale = False ;
	png_bytep     *row_pointers, row;
	unsigned int  y;
	size_t		  row_bytes, offset ;
	static ASImage 	 *im = NULL ;
	START_TIME(started);

	im = NULL ;
	if ((fp = open_image_file(path)) == NULL)
		return NULL;
	/* Create and initialize the png_struct with the desired error handler
	 * functions.  If you want to use the default stderr and longjump method,
	 * you can supply NULL for the last three parameters.  We also supply the
	 * the compiler header file version, so that we know if the application
	 * was compiled with a compatible version of the library.  REQUIRED
	 */
	if((png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) != NULL )
	{
		/* Allocate/initialize the memory for image information.  REQUIRED. */
		if( (info_ptr = png_create_info_struct (png_ptr)) != NULL )
		{
		  	/* Set error handling if you are using the setjmp/longjmp method (this is
			 * the normal method of doing things with libpng).  REQUIRED unless you
			 * set up your own error handlers in the png_create_read_struct() earlier.
			 */
			if ( !setjmp (png_ptr->jmpbuf))
			{
				ASFlagType rgb_flags = ASStorage_RLEDiffCompress|ASStorage_32Bit ;
				
				png_init_io (png_ptr, fp);
		    	png_read_info (png_ptr, info_ptr);
				png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);

				if (bit_depth < 8)
				{/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
				  * byte into separate bytes (useful for paletted and grayscale images).
				  */
					if( bit_depth == 1 ) 
						set_flags( rgb_flags, ASStorage_Bitmap );
					png_set_packing (png_ptr);
				}else if (bit_depth == 16)
				{/* tell libpng to strip 16 bit/color files down to 8 bits/color */
					png_set_strip_16 (png_ptr);
				}
				bit_depth = 8;

				/* Expand paletted colors into true RGB triplets */
				if (color_type == PNG_COLOR_TYPE_PALETTE)
				{
					png_set_expand (png_ptr);
					color_type = PNG_COLOR_TYPE_RGB;
				}

				/* Expand paletted or RGB images with transparency to full alpha channels
				 * so the data will be available as RGBA quartets.
		 		 */
   				if( color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_GRAY )
   				{
				   	if( png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
					{
						png_set_expand(png_ptr);
						color_type |= PNG_COLOR_MASK_ALPHA;
					}
   				}else
				{
					png_set_filler( png_ptr, 0xFF, PNG_FILLER_AFTER );
					color_type |= PNG_COLOR_MASK_ALPHA;
				}

/*				if( color_type == PNG_COLOR_TYPE_RGB )
					color_type = PNG_COLOR_TYPE_RGB_ALPHA ;
   				else
					color_type = PNG_COLOR_TYPE_GRAY_ALPHA ;
  */
				if (png_get_sRGB (png_ptr, info_ptr, &intent))
                    png_set_gamma (png_ptr, params->gamma, DEFAULT_PNG_IMAGE_GAMMA);
				else if (png_get_gAMA (png_ptr, info_ptr, &image_gamma))
					png_set_gamma (png_ptr, params->gamma, image_gamma);
				else
                    png_set_gamma (png_ptr, params->gamma, DEFAULT_PNG_IMAGE_GAMMA);

				/* Optional call to gamma correct and add the background to the palette
				 * and update info structure.  REQUIRED if you are expecting libpng to
				 * update the palette for you (ie you selected such a transform above).
				 */
				png_read_update_info (png_ptr, info_ptr);

				im = create_asimage( width, height, params->compression );
				do_alpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0 );
				grayscale = ( color_type == PNG_COLOR_TYPE_GRAY_ALPHA ||
				              color_type == PNG_COLOR_TYPE_GRAY) ;

				if( !do_alpha && grayscale ) 
					clear_flags( rgb_flags, ASStorage_32Bit );
				else
					prepare_scanline( im->width, 0, &buf, False );

				row_bytes = png_get_rowbytes (png_ptr, info_ptr);
				/* allocating big chunk of memory at once, to enable mmap
				 * that will release memory to system right after free() */
				row_pointers = safemalloc( height * sizeof( png_bytep ) + row_bytes * height );
				row = (png_bytep)(row_pointers + height) ;
				for (offset = 0, y = 0; y < height; y++, offset += row_bytes)
					row_pointers[y] = row + offset;

				/* The easiest way to read the image: */
				png_read_image (png_ptr, row_pointers);
				for (y = 0; y < height; y++)
				{
					if( do_alpha || !grayscale ) 
					{	
						raw2scanline( row_pointers[y], &buf, NULL, buf.width, grayscale, do_alpha );
						im->channels[IC_RED][y] = store_data( NULL, (CARD8*)buf.red, buf.width*4, rgb_flags, 0);
					}else
						im->channels[IC_RED][y] = store_data( NULL, row_pointers[y], im->width, rgb_flags, 0);
					
					if( grayscale ) 
					{	
						im->channels[IC_GREEN][y] = dup_data( NULL, im->channels[IC_RED][y] );
						im->channels[IC_BLUE][y]  = dup_data( NULL, im->channels[IC_RED][y] );
					}else
					{
						im->channels[IC_GREEN][y] = store_data( NULL, (CARD8*)buf.green, buf.width*4, rgb_flags, 0);	
						im->channels[IC_BLUE][y] = store_data( NULL, (CARD8*)buf.blue, buf.width*4, rgb_flags, 0);
					}	 

					if( do_alpha )
					{
						int has_zero = False, has_nozero = False ;
						register unsigned int i;
						for ( i = 0 ; i < buf.width ; ++i)
						{
							if( buf.alpha[i] != 0x00FF )
							{	
								if( buf.alpha[i] == 0 )
									has_zero = True ;
								else
								{	
									has_nozero = True ;
									break;
								}
							}		
						}
						if( has_zero || has_nozero ) 
						{
							ASFlagType alpha_flags = ASStorage_32Bit|ASStorage_RLEDiffCompress ;
							if( !has_nozero ) 
								set_flags( alpha_flags, ASStorage_Bitmap );
							im->channels[IC_ALPHA][y] = store_data( NULL, (CARD8*)buf.alpha, buf.width*4, alpha_flags, 0);
						}
					}
				}
				free (row_pointers);
				if( do_alpha || !grayscale ) 
					free_scanline(&buf, True);
				/* read rest of file, and get additional chunks in info_ptr - REQUIRED */
				png_read_end (png_ptr, info_ptr);
		  	}
		}
		/* clean up after the read, and free any memory allocated - REQUIRED */
		png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp) NULL);
		if (info_ptr)
			free (info_ptr);
	}
	/* close the file */
	fclose (fp);
#if defined(LOCAL_DEBUG) && !defined(NO_DEBUG_OUTPUT)
print_asimage( im, ASFLAGS_EVERYTHING, __FUNCTION__, __LINE__ );
#endif
	SHOW_TIME("image loading",started);
	return im ;
}
#else 			/* PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG */
ASImage *
png2ASImage( const char * path, ASImageImportParams *params )
{
	show_error( "unable to load file \"%s\" - PNG image format is not supported.\n", path );
	return NULL ;
}

#endif 			/* PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG PNG */
/***********************************************************************************/


/***********************************************************************************/
#ifdef HAVE_JPEG     /* JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG */
struct my_error_mgr
{
	struct jpeg_error_mgr pub;				   /* "public" fields */
	jmp_buf       setjmp_buffer;			   /* for return to caller */
};
typedef struct my_error_mgr *my_error_ptr;

METHODDEF (void)
my_error_exit (j_common_ptr cinfo)
{
	/* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
	my_error_ptr  myerr = (my_error_ptr) cinfo->err;
	/* Always display the message. */
	/* We could postpone this until after returning, if we chose. */
	(*cinfo->err->output_message) (cinfo);
	/* Return control to the setjmp point */
	longjmp (myerr->setjmp_buffer, 1);
}

ASImage *
jpeg2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im ;
	/* This struct contains the JPEG decompression parameters and pointers to
	 * working space (which is allocated as needed by the JPEG library).
	 */
	struct jpeg_decompress_struct cinfo;
	/* We use our private extension JPEG error handler.
	 * Note that this struct must live as long as the main JPEG parameter
	 * struct, to avoid dangling-pointer problems.
	 */
	struct my_error_mgr jerr;
	/* More stuff */
	FILE         *infile;					   /* source file */
	JSAMPARRAY    buffer;					   /* Output row buffer */
	ASScanline    buf;
	int y;
	START_TIME(started);
 /*	register int i ;*/

	/* we want to open the input file before doing anything else,
	 * so that the setjmp() error recovery below can assume the file is open.
	 * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
	 * requires it in order to read binary files.
	 */

	if ((infile = open_image_file(path)) == NULL)
		return NULL;

	/* Step 1: allocate and initialize JPEG decompression object */
	/* We set up the normal JPEG error routines, then override error_exit. */
	cinfo.err = jpeg_std_error (&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	/* Establish the setjmp return context for my_error_exit to use. */
	if (setjmp (jerr.setjmp_buffer))
	{
		/* If we get here, the JPEG code has signaled an error.
		   * We need to clean up the JPEG object, close the input file, and return.
		 */
		jpeg_destroy_decompress (&cinfo);
		fclose (infile);
		return NULL;
	}
	/* Now we can initialize the JPEG decompression object. */
	jpeg_create_decompress (&cinfo);
	/* Step 2: specify data source (eg, a file) */
	jpeg_stdio_src (&cinfo, infile);
	/* Step 3: read file parameters with jpeg_read_header() */
	(void)jpeg_read_header (&cinfo, TRUE);
	/* We can ignore the return value from jpeg_read_header since
	 *   (a) suspension is not possible with the stdio data source, and
	 *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
	 * See libjpeg.doc for more info.
	 */

	/* Step 4: set parameters for decompression */
	/* Adjust default decompression parameters */
	cinfo.quantize_colors = FALSE;		       /* we don't want no stinking colormaps ! */
	cinfo.output_gamma = params->gamma;
	/* Step 5: Start decompressor */
	(void)jpeg_start_decompress (&cinfo);
	LOCAL_DEBUG_OUT("stored image size %dx%d", cinfo.output_width,  cinfo.output_height);

	im = create_asimage( cinfo.output_width,  cinfo.output_height, params->compression );
	
	if( cinfo.output_components != 1 ) 
		prepare_scanline( im->width, 0, &buf, False );

	/* Make a one-row-high sample array that will go away when done with image */
	buffer =(*cinfo.mem->alloc_sarray)((j_common_ptr) & cinfo, JPOOL_IMAGE,
										cinfo.output_width * cinfo.output_components, 1);

	/* Step 6: while (scan lines remain to be read) */
	SHOW_TIME("loading initialization",started);
	y = -1 ;
	/*cinfo.output_scanline*/
/*	for( i = 0 ; i < im->width ; i++ )	fprintf( stderr, "%3.3d    ", i );
	fprintf( stderr, "\n");
 */
 	while ( ++y < (int)cinfo.output_height )
	{
		/* jpeg_read_scanlines expects an array of pointers to scanlines.
		 * Here the array is only one element long, but you could ask for
		 * more than one scanline at a time if that's more convenient.
		 */
		(void)jpeg_read_scanlines (&cinfo, buffer, 1);
		if( cinfo.output_components==1 ) 
		{	
			apply_gamma( (CARD8*)buffer[0], params->gamma_table, im->width );
			im->channels[IC_RED][y] = store_data( NULL, (CARD8*)buffer[0], im->width, ASStorage_RLEDiffCompress, 0);
			im->channels[IC_GREEN][y] = dup_data( NULL, im->channels[IC_RED][y] );
			im->channels[IC_BLUE][y]  = dup_data( NULL, im->channels[IC_RED][y] );
		}else
		{		   
			raw2scanline( (CARD8*)buffer[0], &buf, params->gamma_table, im->width, (cinfo.output_components==1), False);
			im->channels[IC_RED][y] = store_data( NULL, (CARD8*)buf.red, buf.width*4, ASStorage_32BitRLE, 0);
			im->channels[IC_GREEN][y] = store_data( NULL, (CARD8*)buf.green, buf.width*4, ASStorage_32BitRLE, 0);
			im->channels[IC_BLUE][y] = store_data( NULL, (CARD8*)buf.blue, buf.width*4, ASStorage_32BitRLE, 0);
		}
/*		fprintf( stderr, "src:");
		for( i = 0 ; i < im->width ; i++ )
			fprintf( stderr, "%2.2X%2.2X%2.2X ", buffer[0][i*3], buffer[0][i*3+1], buffer[0][i*3+2] );
		fprintf( stderr, "\ndst:");
		for( i = 0 ; i < im->width ; i++ )
			fprintf( stderr, "%2.2X%2.2X%2.2X ", buf.red[i], buf.green[i], buf.blue[i] );
		fprintf( stderr, "\n");
 */
	}
	if( cinfo.output_components != 1 ) 
		free_scanline(&buf, True);
	SHOW_TIME("read",started);

	/* Step 7: Finish decompression */
	/* we must abort the decompress if not all lines were read */
	if (cinfo.output_scanline < cinfo.output_height)
		jpeg_abort_decompress (&cinfo);
	else
		(void)jpeg_finish_decompress (&cinfo);
	/* We can ignore the return value since suspension is not possible
	 * with the stdio data source.
	 */
	/* Step 8: Release JPEG decompression object */
	/* This is an important step since it will release a good deal of memory. */
	jpeg_destroy_decompress (&cinfo);
	/* After finish_decompress, we can close the input file.
	 * Here we postpone it until after no more JPEG errors are possible,
	 * so as to simplify the setjmp error logic above.  (Actually, I don't
	 * think that jpeg_destroy can do an error exit, but why assume anything...)
	 */
	fclose (infile);
	/* At this point you may want to check to see whether any corrupt-data
	 * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
	 */
	SHOW_TIME("image loading",started);
	LOCAL_DEBUG_OUT("done loading JPEG image \"%s\"", path);
	return im ;
}
#else 			/* JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG */
ASImage *
jpeg2ASImage( const char * path, ASImageImportParams *params )
{
	show_error( "unable to load file \"%s\" - JPEG image format is not supported.\n", path );
	return NULL ;
}

#endif 			/* JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG JPEG */
/***********************************************************************************/

/***********************************************************************************/
/* XCF - GIMP's native file format : 											   */

ASImage *
xcf2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im = NULL ;
	/* More stuff */
	FILE         *infile;					   /* source file */
	XcfImage  *xcf_im;
	START_TIME(started);

	/* we want to open the input file before doing anything else,
	 * so that the setjmp() error recovery below can assume the file is open.
	 * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
	 * requires it in order to read binary files.
	 */
	if ((infile = open_image_file(path)) == NULL)
		return NULL;

	xcf_im = read_xcf_image( infile );
	fclose( infile );

	if( xcf_im == NULL )
		return NULL;

	LOCAL_DEBUG_OUT("stored image size %ldx%ld", xcf_im->width,  xcf_im->height);
#ifdef LOCAL_DEBUG
	print_xcf_image( xcf_im );
#endif
	{/* TODO : temporary workaround untill we implement layers merging */
		XcfLayer *layer = xcf_im->layers ;
		while ( layer )
		{
			if( layer->hierarchy )
				if( layer->hierarchy->image )
					if( layer->hierarchy->width == xcf_im->width &&
						layer->hierarchy->height == xcf_im->height )
					{
						im = layer->hierarchy->image ;
						layer->hierarchy->image = NULL ;
					}
			layer = layer->next ;
		}
	}
 	free_xcf_image(xcf_im);

	SHOW_TIME("image loading",started);
	return im ;
}

/***********************************************************************************/
/* PPM/PNM file format : 											   				   */
ASImage *
ppm2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im = NULL ;
	/* More stuff */
	FILE         *infile;					   /* source file */
	ASScanline    buf;
	int y;
	unsigned int type = 0, width = 0, height = 0, colors = 0;
#define PPM_BUFFER_SIZE 71                     /* Sun says that no line should be longer then this */
	char buffer[PPM_BUFFER_SIZE];
	START_TIME(started);

	if ((infile = open_image_file(path)) == NULL)
		return NULL;

	if( fgets( &(buffer[0]), PPM_BUFFER_SIZE, infile ) )
	{
		if( buffer[0] == 'P' )
			switch( buffer[1] )
			{    /* we only support RAWBITS formats : */
					case '5' : 	type= 5 ; break ;
					case '6' : 	type= 6 ; break ;
					case '8' : 	type= 8 ; break ;
				default:
					show_error( "invalid or unsupported PPM/PNM file format in image file \"%s\"", path );
			}
		if( type > 0 )
		{
			while ( fgets( &(buffer[0]), PPM_BUFFER_SIZE, infile ) )
			{
				if( buffer[0] != '#' )
				{
					register int i = 0;
					if( width > 0 )
					{
						colors = atoi(&(buffer[i]));
						break;
					}
					width = atoi( &(buffer[i]) );
					while ( buffer[i] != '\0' && !isspace((int)buffer[i]) ) ++i;
					while ( isspace((int)buffer[i]) ) ++i;
					if( buffer[i] != '\0')
						height = atoi(&(buffer[i]));
				}
			}
		}
	}

	if( type > 0 && colors <= 255 &&
		width > 0 && width < MAX_IMPORT_IMAGE_SIZE &&
		height > 0 && height < MAX_IMPORT_IMAGE_SIZE )
	{
		CARD8 *data ;
		size_t row_size = width * ((type==6)?3:((type==8)?4:1));

		data = safemalloc( row_size );

		LOCAL_DEBUG_OUT("stored image size %dx%d", width,  height);
		im = create_asimage( width,  height, params->compression );
		prepare_scanline( im->width, 0, &buf, False );
		y = -1 ;
		/*cinfo.output_scanline*/
		while ( ++y < (int)height )
		{
			if( fread( data, sizeof (char), row_size, infile ) < row_size )
				break;

			raw2scanline( data, &buf, params->gamma_table, im->width, (type==5), (type==8));

			asimage_add_line (im, IC_RED,   buf.red  , y);
			asimage_add_line (im, IC_GREEN, buf.green, y);
			asimage_add_line (im, IC_BLUE,  buf.blue , y);
			if( type == 8 )
				asimage_add_line (im, IC_ALPHA,   buf.alpha  , y);
		}
		free_scanline(&buf, True);
		free( data );
	}
	fclose( infile );
	SHOW_TIME("image loading",started);
	return im ;
}

/***********************************************************************************/
/* Windows BMP file format :   									   				   */
static size_t
bmp_read32 (FILE *fp, CARD32 *data, int count)
{
  	size_t total = count;
	if( count > 0 )
	{
#ifdef WORDS_BIGENDIAN                         /* BMPs are encoded as Little Endian */
		CARD8 *raw = (CARD8*)data ;
#endif
		total = fread((char*) data, sizeof (CARD8), count<<2, fp)>>2;
		count = 0 ;
#ifdef WORDS_BIGENDIAN                         /* BMPs are encoded as Little Endian */
		while( count < total )
		{
			data[count] = (raw[0]<<24)|(raw[1]<<16)|(raw[2]<<8)|raw[3];
			++count ;
			raw += 4 ;
		}
#endif
	}
	return total;
}

static size_t
bmp_read16 (FILE *fp, CARD16 *data, int count)
{
  	size_t total = count;
	if( count > 0 )
	{
#ifdef WORDS_BIGENDIAN                         /* BMPs are encoded as Little Endian */
		CARD8 *raw = (CARD8*)data ;
#endif
		total = fread((char*) data, sizeof (CARD8), count<<1, fp)>>1;
		count = 0 ;
#ifdef WORDS_BIGENDIAN                         /* BMPs are encoded as Little Endian */
		while( count < total )
		{
			data[count] = (raw[0]<<16)|raw[1];
			++count ;
			raw += 2 ;
		}
#endif
	}
	return total;
}


ASImage *
read_bmp_image( FILE *infile, size_t data_offset, BITMAPINFOHEADER *bmp_info,
				ASScanline *buf, CARD8 *gamma_table,
				unsigned int width, unsigned int height,
				Bool add_colormap, unsigned int compression )
{
	Bool success = False ;
	CARD8 *cmap = NULL ;
	int cmap_entries = 0, cmap_entry_size = 4, row_size ;
	int y;
	ASImage *im = NULL ;
	CARD8 *data ;
	int direction = -1 ;

	if( bmp_read32( infile, &bmp_info->biSize, 1 ) )
	{
		if( bmp_info->biSize == 40 )
		{/* long header */
			bmp_read32( infile, (CARD32*)&bmp_info->biWidth, 2 );
			bmp_read16( infile, &bmp_info->biPlanes, 2 );
			bmp_info->biCompression = 1 ;
			success = (bmp_read32( infile, &bmp_info->biCompression, 6 )==6);
		}else
		{
			CARD16 dumm[2] ;
			bmp_read16( infile, &dumm[0], 2 );
			bmp_info->biWidth = dumm[0] ;
			bmp_info->biHeight = dumm[1] ;
			success = ( bmp_read16( infile, &bmp_info->biPlanes, 2 ) == 2 );
			bmp_info->biCompression = 0 ;
		}
	}
#ifdef LOCAL_DEBUG
	fprintf( stderr, "bmp.info.biSize = %ld(0x%lX)\n", bmp_info->biSize, bmp_info->biSize );
	fprintf( stderr, "bmp.info.biWidth = %ld\nbmp.info.biHeight = %ld\n",  bmp_info->biWidth,  bmp_info->biHeight );
	fprintf( stderr, "bmp.info.biPlanes = %d\nbmp.info.biBitCount = %d\n", bmp_info->biPlanes, bmp_info->biBitCount );
	fprintf( stderr, "bmp.info.biCompression = %ld\n", bmp_info->biCompression );
	fprintf( stderr, "bmp.info.biSizeImage = %ld\n", bmp_info->biSizeImage );
#endif
	if( ((int)(bmp_info->biHeight)) < 0 )
		direction = 1 ;
	if( height == 0 )
		height  = direction == 1 ? -((long)(bmp_info->biHeight)):bmp_info->biHeight ;
	if( width == 0 )
		width = bmp_info->biWidth ;

	if( !success || bmp_info->biCompression != 0 ||
		width > MAX_IMPORT_IMAGE_SIZE ||
		height > MAX_IMPORT_IMAGE_SIZE )
	{
		return NULL;
	}
	if( bmp_info->biBitCount < 16 )
		cmap_entries = 0x01<<bmp_info->biBitCount ;

	if( bmp_info->biSize != 40 )
		cmap_entry_size = 3;
	if( cmap_entries )
	{
		cmap = safemalloc( cmap_entries * cmap_entry_size );
		fread(cmap, sizeof (CARD8), cmap_entries * cmap_entry_size, infile);
	}

	if( add_colormap )
		data_offset += cmap_entries*cmap_entry_size ;

	fseek( infile, data_offset, SEEK_SET );
	row_size = (width*bmp_info->biBitCount)>>3 ;
	if( row_size == 0 )
		row_size = 1 ;
	else
		row_size = (row_size+3)/4 ;            /* everything is aligned by 32 bits */
	row_size *= 4 ;                            /* in bytes  */
	data = safemalloc( row_size );

	im = create_asimage( width,  height, compression );
	/* Window BMP files are little endian  - we need to swap Red and Blue */
	prepare_scanline( im->width, 0, buf, True );

	y =( direction == 1 )?0:height-1 ;
	while( y >= 0 && y < (int)height)
	{
		if( fread( data, sizeof (char), row_size, infile ) < (unsigned int)row_size )
			break;
 		dib_data_to_scanline(buf, bmp_info, gamma_table, data, cmap, cmap_entry_size); 
		asimage_add_line (im, IC_RED,   buf->red  , y);
		asimage_add_line (im, IC_GREEN, buf->green, y);
		asimage_add_line (im, IC_BLUE,  buf->blue , y);
		y += direction ;
	}
	free( data );
	if( cmap )
		free( cmap );
	return im ;
}

ASImage *
bmp2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im = NULL ;
	/* More stuff */
	FILE         *infile;					   /* source file */
	ASScanline    buf;
	BITMAPFILEHEADER  bmp_header ;
	BITMAPINFOHEADER  bmp_info;
	START_TIME(started);


	if ((infile = open_image_file(path)) == NULL)
		return NULL;

	bmp_header.bfType = 0 ;
	if( bmp_read16( infile, &bmp_header.bfType, 1 ) )
		if( bmp_header.bfType == BMP_SIGNATURE )
			if( bmp_read32( infile, &bmp_header.bfSize, 3 ) == 3 )
				im = read_bmp_image( infile, bmp_header.bfOffBits, &bmp_info, &buf, params->gamma_table, 0, 0, False, params->compression );
#ifdef LOCAL_DEBUG
	fprintf( stderr, "bmp.header.bfType = 0x%X\nbmp.header.bfSize = %ld\nbmp.header.bfOffBits = %ld(0x%lX)\n",
					  bmp_header.bfType, bmp_header.bfSize, bmp_header.bfOffBits, bmp_header.bfOffBits );
#endif
	if( im != NULL )
		free_scanline( &buf, True );
	else
		show_error( "invalid or unsupported BMP format in image file \"%s\"", path );

	fclose( infile );
	SHOW_TIME("image loading",started);
	return im ;
}

/***********************************************************************************/
/* Windows ICO/CUR file format :   									   			   */

ASImage *
ico2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im = NULL ;
	/* More stuff */
	FILE         *infile;					   /* source file */
	ASScanline    buf;
	int y, mask_bytes;
    CARD8  *and_mask;
	START_TIME(started);
	struct IconDirectoryEntry {
    	CARD8  bWidth;
    	CARD8  bHeight;
    	CARD8  bColorCount;
    	CARD8  bReserved;
    	CARD16  wPlanes;
    	CARD16  wBitCount;
    	CARD32 dwBytesInRes;
    	CARD32 dwImageOffset;
	};
	struct ICONDIR {
    	CARD16          idReserved;
    	CARD16          idType;
    	CARD16          idCount;
	} icon_dir;
   	struct IconDirectoryEntry  icon;
	BITMAPINFOHEADER bmp_info;

	if ((infile = open_image_file(path)) == NULL)
		return NULL;

	icon_dir.idType = 0 ;
	if( bmp_read16( infile, &icon_dir.idReserved, 3 ) == 3)
		if( icon_dir.idType == 1 || icon_dir.idType == 2)
		{
			fread( &(icon.bWidth), sizeof(CARD8),4,infile );
			bmp_read16( infile, &(icon.wPlanes), 2 );
			if( bmp_read32( infile, &(icon.dwBytesInRes), 2 ) == 2 )
			{
				fseek( infile, icon.dwImageOffset, SEEK_SET );
				im = read_bmp_image( infile, icon.dwImageOffset+40+(icon.bColorCount*4), &bmp_info, &buf, params->gamma_table,
					                 icon.bWidth, icon.bHeight, (icon.bColorCount==0), params->compression );
			}
		}
#ifdef LOCAL_DEBUG
	fprintf( stderr, "icon.dir.idType = 0x%X\nicon.dir.idCount = %d\n",  icon_dir.idType, icon_dir.idCount );
	fprintf( stderr, "icon[1].bWidth = %d(0x%X)\n",  icon.bWidth,  icon.bWidth );
	fprintf( stderr, "icon[1].bHeight = %d(0x%X)\n",  icon.bHeight,  icon.bHeight );
	fprintf( stderr, "icon[1].bColorCount = %d\n",  icon.bColorCount );
	fprintf( stderr, "icon[1].dwImageOffset = %ld(0x%lX)\n",  icon.dwImageOffset,  icon.dwImageOffset );
    fprintf( stderr, "icon[1].bmp_size = %ld\n",  icon.dwBytesInRes );
    fprintf( stderr, "icon[1].dwBytesInRes = %ld\n",  icon.dwBytesInRes );
#endif
	if( im != NULL )
	{
        mask_bytes = ((icon.bWidth>>3)+3)/4 ;    /* everything is aligned by 32 bits */
        mask_bytes *= 4 ;                      /* in bytes  */
        and_mask = safemalloc( mask_bytes );
        for( y = icon.bHeight-1 ; y >= 0 ; y-- )
		{
			int x ;
            if( fread( and_mask, sizeof (CARD8), mask_bytes, infile ) < (unsigned int)mask_bytes )
				break;
			for( x = 0 ; x < icon.bWidth ; ++x )
            {
				buf.alpha[x] = (and_mask[x>>3]&(0x80>>(x&0x7)))? 0x0000 : 0x00FF ;
            }
			im->channels[IC_ALPHA][y]  = store_data( NULL, (CARD8*)buf.alpha, im->width*4, 
													 ASStorage_32BitRLE|ASStorage_Bitmap, 0);
		}
        free( and_mask );
		free_scanline( &buf, True );
	}else
		show_error( "invalid or unsupported ICO format in image file \"%s\"", path );

	fclose( infile );
	SHOW_TIME("image loading",started);
	return im ;
}

/***********************************************************************************/
#ifdef HAVE_GIF		/* GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF */

int
gif_interlaced2y(int line /* 0 -- (height - 1) */, int height)
{
   	int passed_lines = 0;
   	int lines_in_current_pass;
   	/* pass 1 */
   	lines_in_current_pass = height / 8 + (height%8?1:0);
   	if (line < lines_in_current_pass) 
    	return line * 8;
   
   	passed_lines = lines_in_current_pass;
   	/* pass 2 */
   	if (height > 4) 
   	{
      	lines_in_current_pass = (height - 4) / 8 + ((height - 4)%8 ? 1 : 0);
      	if (line < lines_in_current_pass + passed_lines) 
         	return 4 + 8*(line - passed_lines);
      	passed_lines += lines_in_current_pass;
   	}
   	/* pass 3 */
   	if (height > 2) 
   	{
      	lines_in_current_pass = (height - 2) / 4 + ((height - 2)%4 ? 1 : 0);
      	if (line < lines_in_current_pass + passed_lines) 
        	return 2 + 4*(line - passed_lines);
    	passed_lines += lines_in_current_pass;
   	}
	return 1 + 2*(line - passed_lines);
}


ASImage *
gif2ASImage( const char * path, ASImageImportParams *params )
{
	FILE			   *fp ;
	int					status = GIF_ERROR;
	GifFileType        *gif;
	ASImage 	 	   *im = NULL ;
	int  		transparent = -1 ;
	unsigned int  		y;
	unsigned int		width = 0, height = 0;
	ColorMapObject     *cmap = NULL ;

	START_TIME(started);

	if ((fp = open_image_file(path)) == NULL)
		return NULL;
	if( (gif = open_gif_read(fp)) != NULL )
	{
		SavedImage	*sp = NULL ;
		int count = 0 ;
		if( (status = get_gif_saved_images(gif, params->subimage, &sp, &count )) == GIF_OK )
		{
			GifPixelType *row_pointer ;
#ifdef DEBUG_TRANSP_GIF
			fprintf( stderr, "Ext block = %p, count = %d\n", sp->ExtensionBlocks, sp->ExtensionBlockCount );
#endif
			if( sp->ExtensionBlocks )
				for ( y = 0; y < (unsigned int)sp->ExtensionBlockCount; y++)
				{
#ifdef DEBUG_TRANSP_GIF
					fprintf( stderr, "%d: func = %X, bytes[0] = 0x%X\n", y, sp->ExtensionBlocks[y].Function, sp->ExtensionBlocks[y].Bytes[0]);
#endif
					if( sp->ExtensionBlocks[y].Function == 0xf9 &&
			 			(sp->ExtensionBlocks[y].Bytes[0]&0x01))
					{
			   		 	transparent = ((unsigned int) sp->ExtensionBlocks[y].Bytes[GIF_GCE_TRANSPARENCY_BYTE])&0x00FF;
#ifdef DEBUG_TRANSP_GIF
						fprintf( stderr, "transp = %u\n", transparent );
#endif
					}
				}
			cmap = gif->SColorMap ;

			cmap = (sp->ImageDesc.ColorMap == NULL)?gif->SColorMap:sp->ImageDesc.ColorMap;
		    width = sp->ImageDesc.Width;
		    height = sp->ImageDesc.Height;

			if( cmap != NULL && (row_pointer = (unsigned char*)sp->RasterBits) != NULL &&
			    width < MAX_IMPORT_IMAGE_SIZE && height < MAX_IMPORT_IMAGE_SIZE )
			{
				int bg_color =   gif->SBackGroundColor ;
                int interlaced = sp->ImageDesc.Interlace;
                int image_y;
				CARD8 		 *r = NULL, *g = NULL, *b = NULL, *a = NULL ;
				r = safemalloc( width );	   
				g = safemalloc( width );	   
				b = safemalloc( width );	   
				a = safemalloc( width );

				im = create_asimage( width, height, params->compression );
				for (y = 0; y < height; ++y)
				{
					unsigned int x ;
					Bool do_alpha = False ;
                    image_y = interlaced ? gif_interlaced2y(y, height):y;
					for (x = 0; x < width; ++x)
					{
						int c = row_pointer[x];
      					if ( c == transparent)
						{
							c = bg_color ;
							do_alpha = True ;
							a[x] = 0 ;
						}else
							a[x] = 0x00FF ;
						
						r[x] = cmap->Colors[c].Red;
		        		g[x] = cmap->Colors[c].Green;
						b[x] = cmap->Colors[c].Blue;
	        		}
					row_pointer += x ;
					im->channels[IC_RED][image_y]  = store_data( NULL, r, width, ASStorage_RLEDiffCompress, 0);
				 	im->channels[IC_GREEN][image_y] = store_data( NULL, g, width, ASStorage_RLEDiffCompress, 0);	
					im->channels[IC_BLUE][image_y]  = store_data( NULL, b, width, ASStorage_RLEDiffCompress, 0);
					if( do_alpha )
						im->channels[IC_ALPHA][image_y]  = store_data( NULL, a, im->width, ASStorage_RLEDiffCompress|ASStorage_Bitmap, 0);
				}
				free(a);
				free(b);
				free(g);
				free(r);
			}
			free_gif_saved_images( sp, count );
		}else
			ASIM_PrintGifError();
		DGifCloseFile(gif);
		fclose( fp );
	}
	SHOW_TIME("image loading",started);
	return im ;
}
#else 			/* GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF */
ASImage *
gif2ASImage( const char * path, ASImageImportParams *params )
{
	show_error( "unable to load file \"%s\" - missing GIF image format libraries.\n", path );
	return NULL ;
}
#endif			/* GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF GIF */

#ifdef HAVE_TIFF/* TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF */
ASImage *
tiff2ASImage( const char * path, ASImageImportParams *params )
{
	TIFF 		 *tif ;

	static ASImage 	 *im = NULL ;
	CARD32 *data;
	CARD32 width = 1, height = 1;
	CARD16 depth = 4 ;
	CARD16 bits = 0 ;
	CARD32 rows_per_strip =0 ;
	CARD32 tile_width = 0, tile_length = 0 ;
	CARD16 photo = 0;
	START_TIME(started);

	if ((tif = TIFFOpen(path,"r")) == NULL)
	{
		show_error("cannot open image file \"%s\" for reading. Please check permissions.", path);
		return NULL;
	}

	if( params->subimage > 0 )
		if( !TIFFSetDirectory(tif, params->subimage))
			show_warning("failed to read subimage %d from image file \"%s\". Reading first available instead.", params->subimage, path);

	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
	if( !TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &depth) )
		depth = 3 ;
	if( !TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits) )
		bits = 8 ;
	if( !TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip ) )
		rows_per_strip = height ;	
	if( !TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photo) )
		photo = 0 ;
	
	if( TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width) ||
		TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_length) )
	{
		show_error( "Tiled TIFF image format is not supported yet." );
		return NULL;   
	}		

	if( rows_per_strip == 0 || rows_per_strip > height ) 
		rows_per_strip = height ;
	if( depth <= 0 ) 
		depth = 4 ;
	if( depth <= 2 && get_flags( photo, PHOTOMETRIC_RGB) )
		depth += 2 ;
	LOCAL_DEBUG_OUT( "size = %ldx%ld, depth = %d, bits = %d, rps = %ld, photo = 0x%X", 
					 width, height, depth, bits, rows_per_strip, photo );
	if( width < MAX_IMPORT_IMAGE_SIZE && height < MAX_IMPORT_IMAGE_SIZE )
	{
		if ((data = (CARD32*) _TIFFmalloc(width*rows_per_strip*sizeof(CARD32))) != NULL)
		{
			CARD8 		 *r = NULL, *g = NULL, *b = NULL, *a = NULL ;
			ASFlagType store_flags = ASStorage_RLEDiffCompress	;
			int first_row = 0 ;
			if( bits == 1 ) 
				set_flags( store_flags, ASStorage_Bitmap );
			
			im = create_asimage( width, height, params->compression );
			if( depth == 2 || depth == 4 ) 
				a = safemalloc( width );
			r = safemalloc( width );	   
			if( depth > 2 ) 
			{
				g = safemalloc( width );	   
				b = safemalloc( width );	   
			}	 
			
			while( first_row < height ) 
			{	
				if (TIFFReadRGBAStrip(tif, first_row, (void*)data))
				{
					register CARD32 *row = data ;
					int y = first_row + rows_per_strip ;
					if( y > height ) 
						y = height ;
					while( --y >= first_row )
					{
						int x ;
						for( x = 0 ; x < width ; ++x )
						{
							CARD32 c = row[x] ;
							if( depth == 4 || depth == 2 ) 
								a[x] = TIFFGetA(c);
							r[x]   = TIFFGetR(c);
							if( depth > 2 ) 
							{
								g[x] = TIFFGetG(c);
								b[x]  = TIFFGetB(c);
							}
						}
						im->channels[IC_RED][y]  = store_data( NULL, r, width, store_flags, 0);
						if( depth > 2 ) 
						{
					 		im->channels[IC_GREEN][y] = store_data( NULL, g, width, store_flags, 0);	
							im->channels[IC_BLUE][y]  = store_data( NULL, b, width, store_flags, 0);
						}else
						{
					 		im->channels[IC_GREEN][y] = dup_data( NULL, im->channels[IC_RED][y]);	  
							im->channels[IC_BLUE][y]  = dup_data( NULL, im->channels[IC_RED][y]);
						}		 
					
						if( depth == 4 || depth == 2 ) 
							im->channels[IC_ALPHA][y]  = store_data( NULL, a, width, store_flags, 0);
						row += width ;
					}
				}
				first_row += rows_per_strip ;
		    }
			if( b ) free( b );
			if( g ) free( g );
			if( r ) free( r );
			if( a ) free( a );
			_TIFFfree(data);
		}
	}
	/* close the file */
	TIFFClose(tif);
	SHOW_TIME("image loading",started);

	return im ;
}
#else 			/* TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF */

ASImage *
tiff2ASImage( const char * path, ASImageImportParams *params )
{
	show_error( "unable to load file \"%s\" - missing TIFF image format libraries.\n", path );
	return NULL ;
}
#endif			/* TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF TIFF */


ASImage *
load_xml2ASImage( ASImageManager *imman, const char *path, unsigned int compression )
{
	ASVisual fake_asv ;
	char *slash, *curr_path = NULL ;
	char *doc_str = NULL ;
	ASImage *im = NULL ;

	memset( &fake_asv, 0x00, sizeof(ASVisual) );
	if( (slash = strrchr( path, '/' )) != NULL )
		curr_path = mystrndup( path, slash-path );

	if((doc_str = load_file(path)) == NULL )
		show_error( "unable to load file \"%s\" file is either too big or is not readable.\n", path );
	else
	{
		im = compose_asimage_xml(&fake_asv, imman, NULL, doc_str, 0, 0, None, curr_path);
		free( doc_str );
	}

	if( curr_path )
		free( curr_path );
	return im ;
}


ASImage *
xml2ASImage( const char *path, ASImageImportParams *params )
{
	static ASImage 	 *im = NULL ;
	START_TIME(started);

	im = load_xml2ASImage( NULL, path, params->compression );

	SHOW_TIME("image loading",started);
	return im ;
}
/*************************************************************************/
/* Targa Image format - some stuff borrowed from the GIMP.
 *************************************************************************/
typedef struct ASTGAHeader
{
	CARD8 IDLength ;
	CARD8 ColorMapType;
#define TGA_NoImageData			0
#define TGA_ColormappedImage	1
#define TGA_TrueColorImage		2
#define TGA_BWImage				3
#define TGA_RLEColormappedImage		9
#define TGA_RLETrueColorImage		10
#define TGA_RLEBWImage				11
	CARD8 ImageType;
	struct 
	{
		CARD16 FirstEntryIndex ;
		CARD16 ColorMapLength ;  /* number of entries */ 
		CARD8  ColorMapEntrySize ;  /* number of bits per entry */ 
	}ColormapSpec;
	struct
	{		
		CARD16 XOrigin;
		CARD16 YOrigin;
		CARD16 Width;
		CARD16 Height;
		CARD8  Depth;
#define TGA_LeftToRight		(0x01<<4)
#define TGA_TopToBottom		(0x01<<5)
		CARD8  Descriptor;
	}ImageSpec;

}ASTGAHeader;

typedef struct ASTGAColorMap
{
	int bytes_per_entry;
	int bytes_total ; 
	CARD8 *data ; 
}ASTGAColorMap;

typedef struct ASTGAImageData
{
	int bytes_per_pixel;
	int image_size;
	int bytes_total ; 
	CARD8 *data ; 
}ASTGAImageData;

static Bool load_tga_colormapped(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
		
	return True;
}

static Bool load_tga_truecolor(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
	CARD32 *a = buf->alpha ;
	CARD32 *r = buf->red ;
	CARD32 *g = buf->green ;
	CARD32 *b = buf->blue ;
	int bpp = (tga->ImageSpec.Depth+7)/8;
	int bpl = buf->width*bpp;
	if( fread( read_buf, 1, bpl, infile ) != bpl ) 		   
		return False;
	if( bpp == 3 ) 
	{	
		int i;
		if( gamma_table )
			for( i = 0 ; i < buf->width ; ++i ) 
			{
				b[i] = gamma_table[*(read_buf++)];	
				g[i] = gamma_table[*(read_buf++)];	  
				r[i] = gamma_table[*(read_buf++)];	  
			}	 
		else
			for( i = 0 ; i < buf->width ; ++i ) 
			{
				b[i] = *(read_buf++);	
				g[i] = *(read_buf++);	  
				r[i] = *(read_buf++);	  
			}	 
		set_flags( buf->flags, SCL_DO_RED|SCL_DO_GREEN|SCL_DO_BLUE );
	}else if( bpp == 4 )
	{
		int i;
		for( i = 0 ; i < buf->width ; ++i ) 
		{
			b[i] = *(read_buf++);	
			g[i] = *(read_buf++);	  
			r[i] = *(read_buf++);	  
			a[i] = *(read_buf++);	  
		}	 
		set_flags( buf->flags, SCL_DO_RED|SCL_DO_GREEN|SCL_DO_BLUE|SCL_DO_ALPHA );
	}	 

	return True;
}

static Bool load_tga_bw(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
		
	return True;
}

static Bool load_tga_rle_colormapped(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
		
	return True;
}

static Bool load_tga_rle_truecolor(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
		
	return True;
}

static Bool load_tga_rle_bw(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table )
{
		
	return True;
}



ASImage *
tga2ASImage( const char * path, ASImageImportParams *params )
{
	ASImage *im = NULL ;
	/* More stuff */
	FILE         *infile;					   /* source file */
	ASTGAHeader   tga;
	ASTGAColorMap *cmap = NULL ;
	int width = 1, height = 1;
	START_TIME(started);


	if ((infile = open_image_file(path)) == NULL)
		return NULL;
	if( fread( &tga, 1, 3, infile ) == 3 ) 
	if( fread( &tga.ColormapSpec, 1, 5, infile ) == 5 ) 
	if( fread( &tga.ImageSpec, 1, 10, infile ) == 10 ) 
	{
		Bool success = True ;
		Bool (*load_row_func)(FILE *infile, ASTGAHeader *tga, ASTGAColorMap *cmap, ASScanline *buf, CARD8 *read_buf, CARD8 *gamma_table );

		if( tga.IDLength > 0 ) 
			success = (fseek( infile, tga.IDLength, SEEK_CUR )==0);
		if( success && tga.ColorMapType != 0 ) 
		{
			cmap = safecalloc( 1, sizeof(ASTGAColorMap));
			cmap->bytes_per_entry = (tga.ColormapSpec.ColorMapEntrySize+7)/8;
			cmap->bytes_total = cmap->bytes_per_entry*tga.ColormapSpec.ColorMapLength; 
			cmap->data = safemalloc( cmap->bytes_total);
			success = ( fread( cmap->data, 1, cmap->bytes_total, infile ) == cmap->bytes_total );
		}else if( tga.ImageSpec.Depth != 24 && tga.ImageSpec.Depth != 32 )
			success = False ;
	 
		if( success ) 
		{
			success = False;
			if( tga.ImageType != TGA_NoImageData )
			{	
				width = tga.ImageSpec.Width ; 
				height = tga.ImageSpec.Height ; 
				if( width < MAX_IMPORT_IMAGE_SIZE && height < MAX_IMPORT_IMAGE_SIZE )
					success = True;
			}
		}
		switch( tga.ImageType ) 
		{
			case TGA_ColormappedImage	:load_row_func = load_tga_colormapped ; break ;
			case TGA_TrueColorImage		:load_row_func = load_tga_truecolor ; break ;
			case TGA_BWImage			:load_row_func = load_tga_bw ; break ;
			case TGA_RLEColormappedImage:load_row_func = load_tga_rle_colormapped ; break ;
			case TGA_RLETrueColorImage	:load_row_func = load_tga_rle_truecolor ; break ;
			case TGA_RLEBWImage			:load_row_func = load_tga_rle_bw ; break ;
			default:
				load_row_func = NULL ;
		}	 
		
		if( success && load_row_func != NULL ) 
		{	
			ASImageOutput  *imout ;
			im = create_asimage( width, height, params->compression );
			if((imout = start_image_output( NULL, im, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT)) == NULL )
			{
        		destroy_asimage( &im );
				success = False;
			}else
			{	
				ASScanline    buf;
				int y ;
				CARD8 *read_buf = safemalloc( width*4*2 ); 
				prepare_scanline( im->width, 0, &buf, True );
				if( !get_flags( tga.ImageSpec.Descriptor, TGA_TopToBottom ) )			
					toggle_image_output_direction( imout );
				for( y = 0 ; y < height ; ++y ) 
				{	
					if( !load_row_func( infile, &tga, cmap, &buf, read_buf, params->gamma_table ) )
						break;
					imout->output_image_scanline( imout, &buf, 1);
				}
				stop_image_output( &imout );
				free_scanline( &buf, True );
				free( read_buf );
			}   
		}	  
	}	 
	if( im == NULL )
		show_error( "invalid or unsupported TGA format in image file \"%s\"", path );

	fclose( infile );
	SHOW_TIME("image loading",started);
	return im ;
}
/*************************************************************************/
/* ARGB 																 */
/*************************************************************************/
ASImage *
convert_argb2ASImage( ASVisual *asv, int width, int height, ARGB32 *argb, CARD8 *gamma_table )
{
	ASImage *im = NULL ;
	ASImageOutput  *imout ;
	im = create_asimage( width, height, 100 );
	if((imout = start_image_output( NULL, im, ASA_ASImage, 0, ASIMAGE_QUALITY_DEFAULT)) == NULL )
	{
   		destroy_asimage( &im );
		return NULL;
	}else
	{	
		ASScanline    buf;
		int y ;
		prepare_scanline( im->width, 0, &buf, True );
		for( y = 0 ; y < height ; ++y ) 
		{	  
			int x ;
			for( x = 0 ; x < width ; ++x ) 
			{
				ARGB32 c = argb[x];
				buf.alpha[x] 	= ARGB32_ALPHA8(c);	
				buf.red[x] 	= ARGB32_RED8(c);	  
				buf.green[x] 	= ARGB32_GREEN8(c);	  
				buf.blue[x] 	= ARGB32_BLUE8(c);	  
			}	 
			argb += width ;			
			set_flags( buf.flags, SCL_DO_RED|SCL_DO_GREEN|SCL_DO_BLUE|SCL_DO_ALPHA );
			imout->output_image_scanline( imout, &buf, 1);
		}
		stop_image_output( &imout );
		free_scanline( &buf, True );
	}   
						
	return im ;	
}


ASImage *
argb2ASImage( const char *path, ASImageImportParams *params )
{
	ASVisual fake_asv ;
	long argb_data_len = -1; 
	char *argb_data = NULL ;
	ASImage *im = NULL ;

	memset( &fake_asv, 0x00, sizeof(ASVisual) );

	argb_data = load_binary_file(path, &argb_data_len);
	if(argb_data == NULL || argb_data_len < 8 )
		show_error( "unable to load file \"%s\" file is either too big or is not readable.\n", path );
	else
	{
		int width = ((CARD32*)argb_data)[0] ;
		int height = ((CARD32*)argb_data)[1] ;
		if( 2 + width*height > argb_data_len/sizeof(CARD32))
		{
			show_error( "file \"%s\" is too small for specified image size of %dx%d.\n", path, width, height );
		}else
			im = convert_argb2ASImage( &fake_asv, width, height, (ARGB32*)argb_data+2, params->gamma_table );
	}
	if( argb_data ) 
		free( argb_data );
	
	return im ;
}

