2674 lines
75 KiB
C
2674 lines
75 KiB
C
|
/**
|
||
|
** $Header: /roq/libim/imfile.c 1 11/02/99 4:38p Zaphod $
|
||
|
** Copyright (c) 1989-1995 San Diego Supercomputer Center (SDSC)
|
||
|
** a division of General Atomics, San Diego, California, USA
|
||
|
**
|
||
|
** Users and possessors of this source code are hereby granted a
|
||
|
** nonexclusive, royalty-free copyright and design patent license to
|
||
|
** use this code in individual software. License is not granted for
|
||
|
** commercial resale, in whole or in part, without prior written
|
||
|
** permission from SDSC. This source is provided "AS IS" without express
|
||
|
** or implied warranty of any kind.
|
||
|
**
|
||
|
** For further information contact:
|
||
|
** E-Mail: info@sds.sdsc.edu
|
||
|
**
|
||
|
** Surface Mail: Information Center
|
||
|
** San Diego Supercomputer Center
|
||
|
** P.O. Box 85608
|
||
|
** San Diego, CA 92138-5608
|
||
|
** (619) 534-5000
|
||
|
**/
|
||
|
|
||
|
#define HEADER " $Header: /roq/libim/imfile.c 1 11/02/99 4:38p Zaphod $"
|
||
|
|
||
|
/**
|
||
|
** FILE
|
||
|
** imfile.c - file I/O for the image manipulation library
|
||
|
**
|
||
|
** PROJECT
|
||
|
** libim - SDSC image manipulation library
|
||
|
**
|
||
|
** DESCRIPTION
|
||
|
** imfile.c contains the top level read and write routines of the
|
||
|
** image library and the dispatch table for each format supported.
|
||
|
**
|
||
|
**
|
||
|
** PUBLIC CONTENTS
|
||
|
** d =defined constant
|
||
|
** f =function
|
||
|
** m =defined macro
|
||
|
** t =typedef/struct/union
|
||
|
** v =variable
|
||
|
** ? =other
|
||
|
**
|
||
|
** ImFileQFormat f query the format of a file
|
||
|
** ImFileQFFormat f query the format of a file
|
||
|
**
|
||
|
** ImFileQNFormat f query # of formats
|
||
|
**
|
||
|
** ImFileRead f read an image file from a file descriptor
|
||
|
** ImFileFRead f read an image file from a file pointer
|
||
|
** ImFileWrite f write an image file to a file descriptor
|
||
|
** ImFileFWrite f write an image file to a file pointer
|
||
|
** ImFileFindFormat f find the format info given a format name
|
||
|
**
|
||
|
** imFileQCompression f query the compression scheme of a file
|
||
|
** ImFileFindCompressionScheme f find the scheme info given a name
|
||
|
**
|
||
|
**
|
||
|
** PRIVATE CONTENTS
|
||
|
** IOTYPE d figure the IoType for an unbuffered stream
|
||
|
** FIOTYPE d figure the IoType for a buffered stream
|
||
|
**
|
||
|
** imFileRead f do the real work of read
|
||
|
** imFileSelectMap f select a good write map to use
|
||
|
** imFileReadCompressedFile f Read a compressed file
|
||
|
** imFileWriteCompressedFile f Write a compressed file
|
||
|
**
|
||
|
** HISTORY
|
||
|
** $Log: /roq/libim/imfile.c $
|
||
|
*
|
||
|
* 1 11/02/99 4:38p Zaphod
|
||
|
** Revision 1.44 1995/08/15 08:37:32 bduggan
|
||
|
** Modified ImFileWrite to return the name of the format.
|
||
|
**
|
||
|
** Revision 1.44 1995/08/15 08:37:32 bduggan
|
||
|
** Modified ImFileWrite to return the name of the format.
|
||
|
**
|
||
|
** Revision 1.43 1995/06/30 21:59:39 bduggan
|
||
|
** removed mktemp prototype
|
||
|
** put 'int' between 'static' and the variable name
|
||
|
**
|
||
|
** Revision 1.42 1995/06/29 00:28:04 bduggan
|
||
|
** updated copyright year
|
||
|
**
|
||
|
** Revision 1.41 1995/06/29 00:19:29 bduggan
|
||
|
** changed error message
|
||
|
**
|
||
|
** Revision 1.40 1995/06/16 08:40:19 bduggan
|
||
|
** Improved compression support
|
||
|
**
|
||
|
** Revision 1.39 1995/05/19 13:01:17 bduggan
|
||
|
** Added imcompress interface
|
||
|
**
|
||
|
** Revision 1.38 1995/05/06 01:43:56 bduggan
|
||
|
** Added .Z compression support!
|
||
|
**
|
||
|
** Revision 1.38 1995/05/06 01:43:56 bduggan
|
||
|
** Added .Z compression support!
|
||
|
**
|
||
|
** Revision 1.37 1995/04/03 21:23:57 bduggan
|
||
|
** took out #ifdef NEWMAGIC
|
||
|
**
|
||
|
** Revision 1.36 1995/02/16 21:38:44 bduggan
|
||
|
** Added support checking for 'image compression quality' tag
|
||
|
** Moved ImFileFormatEquivs and ImFileFormatOptions from here
|
||
|
** to imtools.c
|
||
|
**
|
||
|
** Revision 1.35 1995/01/10 23:22:33 bduggan
|
||
|
** Added grouping option for tiles vs. scanlines
|
||
|
**
|
||
|
** Revision 1.34 94/10/03 11:29:33 nadeau
|
||
|
** Updated to ANSI C and C++ compatibility.
|
||
|
** Removed all use of register keyword.
|
||
|
** Minimized use of custom SDSC types (e.g., uchar vs. unsigned char)
|
||
|
** Changed all float arguments to double.
|
||
|
** Added forward declarations.
|
||
|
** Added misc. casts to passify SGI and DEC compilers.
|
||
|
** Changed all macros and defined constants to have names
|
||
|
** starting with IM.
|
||
|
** Rearranged magic number structures for format handlers.
|
||
|
** Made format handler routines static (i.e., local to file).
|
||
|
** Updated comments, adding format descriptions and references.
|
||
|
** Updated indenting on some code.
|
||
|
** Updated copyright message.
|
||
|
**
|
||
|
** Revision 1.33 93/10/22 23:20:17 nadeau
|
||
|
** Added restoration of binary I/O library state around all
|
||
|
** file I/O calls.
|
||
|
**
|
||
|
** Revision 1.32 92/11/24 11:48:58 groening
|
||
|
** Corrected problem that caused the wrong write-map to be
|
||
|
** selected for non-RGB images when the flagsTable requested
|
||
|
** interleaving.
|
||
|
**
|
||
|
** Revision 1.31 92/11/04 11:45:03 groening
|
||
|
** since ImFileFormats got changed around
|
||
|
** I changed all the types dealing with
|
||
|
** ImFileFormats, also added multiple magic
|
||
|
** number capabilities. cheerio!
|
||
|
**
|
||
|
** Revision 1.30 92/10/12 15:55:23 vle
|
||
|
** Added code to handle ImInfo stuff.
|
||
|
**
|
||
|
** Revision 1.29 92/09/03 17:00:55 nadeau
|
||
|
** Fixed a minor typo.
|
||
|
**
|
||
|
** Revision 1.28 92/09/03 16:47:18 vle
|
||
|
** Updated calls to ImVfbGrayRamp() to match new parameters.
|
||
|
**
|
||
|
** Revision 1.27 92/04/09 09:33:11 groening
|
||
|
** In order for the compiler to like the code had to add extern statements.
|
||
|
**
|
||
|
** Revision 1.26 91/10/03 08:59:13 nadeau
|
||
|
** Changed 'interlace' to 'interleave'. Made format arguments
|
||
|
** only show up under -fullhelp.
|
||
|
**
|
||
|
** Revision 1.25 91/03/12 10:59:49 nadeau
|
||
|
** Fixed bug in fopen of temp files for read and write.
|
||
|
**
|
||
|
** Revision 1.24 91/03/08 14:27:01 nadeau
|
||
|
** Totally rewrite. Moved the formats table and declarations into
|
||
|
** a separate file (imfmt.c). Enhanced ImFileRead to automatically
|
||
|
** copy pipe data into a temp file for formats that cannot handle
|
||
|
** pipes directly. Enhanced ImFileWrite to do likewise. Lots of
|
||
|
** changes to ImFileWrite to make it automatically do VFB depth
|
||
|
** conversions prior to calling format write code. It also does
|
||
|
** lots of standard error checking.
|
||
|
**
|
||
|
** Revision 1.23 91/01/30 17:14:36 nadeau
|
||
|
** First attempt at restructuring to support more error checking
|
||
|
** in ImFile before it gets to the format routines.
|
||
|
**
|
||
|
** Revision 1.22 91/01/22 09:48:23 nadeau
|
||
|
** NULL-ed out all info query calls. Added more equiv names for
|
||
|
** various formats.
|
||
|
**
|
||
|
** Revision 1.21 90/11/14 18:35:03 mjb
|
||
|
** Added cgm support
|
||
|
**
|
||
|
** Revision 1.20 90/08/30 10:00:27 moreland
|
||
|
** Added MacPaint support
|
||
|
**
|
||
|
** Revision 1.19 90/08/02 13:32:51 mcleodj
|
||
|
** Added PIC support
|
||
|
**
|
||
|
** Revision 1.18 90/07/31 12:42:39 mjb
|
||
|
** Changed PICT routine names to match those in impict.c
|
||
|
**
|
||
|
** Revision 1.17 90/07/30 16:38:47 moreland
|
||
|
** Enabled Apple PICT format.
|
||
|
** Sorry, no single magic number...
|
||
|
**
|
||
|
** Revision 1.16 90/07/30 16:31:20 nadeau
|
||
|
** Remove 'x' as one of the equivalent names for XWD. Added support for
|
||
|
** the 'x' format used by Stardent for their AVS system.
|
||
|
**
|
||
|
** Revision 1.15 90/07/25 13:32:02 todd
|
||
|
** Add imtiff back in to the list of known formats
|
||
|
**
|
||
|
** Revision 1.14 90/07/24 15:34:23 ferrerj
|
||
|
** Temporarily removed TIFF support.
|
||
|
**
|
||
|
** Revision 1.13 90/07/24 13:51:23 nadeau
|
||
|
** Added XWD support.
|
||
|
**
|
||
|
** Revision 1.12 90/07/23 13:48:34 nadeau
|
||
|
** Added HDF support and prepared for PIC support.
|
||
|
**
|
||
|
** Revision 1.11 90/07/05 11:41:36 todd
|
||
|
** add .tif and .TIF to equivalent name table. Changed tif magic number
|
||
|
** to 4d4d (MBF). 4f4f is LBF.
|
||
|
**
|
||
|
** Revision 1.10 90/07/02 13:18:15 nadeau
|
||
|
** Changed ImTag* to Tag* (new names), added new error handling code, added
|
||
|
** flags table code, entered SYNU format, unifdefed TIFF format, and
|
||
|
** rearranged the names of all read/write calls from ImReadXXX to
|
||
|
** ImXXXRead, etc.
|
||
|
**
|
||
|
** Revision 1.9 90/06/25 14:32:19 nadeau
|
||
|
** Changed ImTag* to Tag* (new names).
|
||
|
**
|
||
|
** Revision 1.8 90/06/25 14:27:11 ferrerj
|
||
|
** Removed #ifdef's from RGB format initialization.
|
||
|
**
|
||
|
** Revision 1.7 90/06/04 10:42:08 nadeau
|
||
|
** Just added some curly braces.
|
||
|
**
|
||
|
** Revision 1.6 90/05/15 16:29:13 todd
|
||
|
** Add imiff.c and imrla.c to tables
|
||
|
**
|
||
|
** Revision 1.5 90/05/15 13:59:30 nadeau
|
||
|
** Removed old format table code between #ifdefs and added more comments.
|
||
|
**
|
||
|
** Revision 1.4 90/05/11 09:53:29 nadeau
|
||
|
** Changed format arg to read/write calls to a character string. Added
|
||
|
** a master file format table with lots of info on the format, including
|
||
|
** magic number and equivalent names. Added format query calls to intuit
|
||
|
** a file's format. Added calls to add the format names to the options
|
||
|
** and equivs lists for the arg package. All additions are #ifdef'ed in.
|
||
|
**
|
||
|
** Revision 1.3 90/04/09 10:58:56 todd
|
||
|
** Added Sun-TAAC IFF format ImReadIff() and ImWriteIff()
|
||
|
**
|
||
|
** Revision 1.2 90/03/28 11:15:17 nadeau
|
||
|
** Dropped the format set/query functions. Added format and flags
|
||
|
** arguments to the read and write calls. Added two new variations
|
||
|
** of the read and write calls: fd and fp read.
|
||
|
**
|
||
|
** Revision 1.1 90/03/06 17:32:14 nadeau
|
||
|
** Initial revision
|
||
|
**
|
||
|
**/
|
||
|
|
||
|
//#include <unistd.h>
|
||
|
//#include <sys/wait.h>
|
||
|
#include "im.h"
|
||
|
#include "iminternal.h"
|
||
|
#include "impref.h"
|
||
|
|
||
|
/**
|
||
|
** CODE CREDITS
|
||
|
** Custom development, Dave Nadeau, San Diego Supercomputer Center, 1990.
|
||
|
** Custom development, Brian Duggan, San Diego Supercomputer Center, 1995.
|
||
|
**/
|
||
|
|
||
|
|
||
|
/* NOTE
|
||
|
*
|
||
|
* Note that here and throughout the library, we try to refer to a file
|
||
|
* compression 'scheme' and an image 'format'; not a compression
|
||
|
* 'format', (and not an image 'scheme') in order to keep things clear.
|
||
|
*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* DEFINES
|
||
|
* IMREADMAGIC
|
||
|
* IMREADFMAGIC
|
||
|
* IMMAXMAGIC
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* These macros are used by input routines in
|
||
|
* imfile.c and imcompress.c to query the magic
|
||
|
* number of a file.
|
||
|
*/
|
||
|
|
||
|
#define IMMAXMAGIC 100 /* Max size magic number */
|
||
|
|
||
|
#define IMREADMAGIC(fd,buf,size,at) \
|
||
|
{ \
|
||
|
long location; /* Saved file location */\
|
||
|
location = lseek( (fd), 0, L_INCR ); \
|
||
|
lseek( (fd), (at), L_SET ); \
|
||
|
BinRead( (fd), (buf), UCHAR, 1, (size) ); \
|
||
|
lseek( (fd), location, L_SET ); \
|
||
|
}
|
||
|
#define IMREADFMAGIC(fp,buf,size,at) \
|
||
|
{ \
|
||
|
long location; /* Saved file location */\
|
||
|
location = fseek( (fp), 0, F_INCR ); \
|
||
|
fseek( (fp), (at), F_SET ); \
|
||
|
BinFRead( (fp), (buf), UCHAR, 1, (size) ); \
|
||
|
fseek( (fp), location, F_SET ); \
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION DECLARATIONS
|
||
|
*/
|
||
|
#ifdef __STDC__
|
||
|
/* extern char *mktemp( char * ); */
|
||
|
|
||
|
static char *imFileQFormat( int, int, FILE *, char * );
|
||
|
static void imFileErrorTags( TagTable * );
|
||
|
static void imFileInfoTags( TagTable * );
|
||
|
static void imFileImageTags( TagTable *, int *, int *, int *, int *,
|
||
|
int *, int *, int *, int *, int *, int * );
|
||
|
static int imFileRead( int, int, FILE *, char *, TagTable *,
|
||
|
TagTable * );
|
||
|
static ImFileFormatWriteMap *imFileSelectMap( ImVfb *, ImFileFormat **, int,
|
||
|
int, int, int, int, int, int, int );
|
||
|
static int imFileWrite( int, int, FILE *, char *, TagTable *,
|
||
|
TagTable * );
|
||
|
static int imFileWriteSingle( int, int, FILE *, ImFileFormat **,
|
||
|
TagTable *, TagTable * );
|
||
|
static int imFileWriteMultiple( int, int, FILE *, ImFileFormat **,
|
||
|
TagTable *, TagTable * );
|
||
|
static int imFileWriteIt( ImFileFormatWriteMap *, int, int, FILE *,
|
||
|
ImFileFormat **, TagTable *, TagTable * );
|
||
|
static int imFileReadCompressedFile(int , int , FILE* , TagTable* , TagTable* , char* );
|
||
|
static int imFileWriteCompressedFile(int, int, FILE*, TagTable*, TagTable*,
|
||
|
ImFileFormatWriteMap *, char* );
|
||
|
static char * imFileQCompression(int ioType, int fd, FILE* fp, TagTable* flagsTable);
|
||
|
#else
|
||
|
extern char *mktemp( );
|
||
|
|
||
|
static char *imFileQFormat( );
|
||
|
static void imFileErrorTags( );
|
||
|
static void imFileInfoTags( );
|
||
|
static void imFileImageTags( );
|
||
|
static int imFileRead( );
|
||
|
static ImFileFormatWriteMap *imFileSelectMap( );
|
||
|
static int imFileWrite( );
|
||
|
static int imFileWriteSingle( );
|
||
|
static int imFileWriteMultiple( );
|
||
|
static int imFileReadCompressedFile();
|
||
|
static int imFileWriteCompressedFile();
|
||
|
static char * imFileQCompression();
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imMakeTempFile()
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Because some files use byte offsets that point hither and yon
|
||
|
* throughout the file, we can't always handle an input or output
|
||
|
* stream "on the fly".
|
||
|
*
|
||
|
* For reads we copy the input stream to a file, then read it.
|
||
|
* For writes we write to a file, then copy it to the output stream.
|
||
|
*
|
||
|
* imMakeTempFile() generates a char* which should be used as the
|
||
|
* a temporary file.
|
||
|
*/
|
||
|
|
||
|
#ifdef __STDC__
|
||
|
static char* imMakeTempFile(void);
|
||
|
#else
|
||
|
static char* imMakeTempFile();
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* MACRO
|
||
|
* IOTYPE - figure the IoType for an unbuffered stream
|
||
|
* FIOTYPE - figure the IoType for a buffered stream
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* These macros determine whether the stream corresponds to a tty,
|
||
|
* pipe, or file. Tty's and pipes must be handled specially (without
|
||
|
* seeking) by the lower level read and write code.
|
||
|
*
|
||
|
* The following cases must be recognized (using input as an example):
|
||
|
*
|
||
|
* Command line Meaning Classification
|
||
|
* tool file read from file file
|
||
|
* tool < file redirect from file file
|
||
|
* tool < /dev/tty read from tty pipe
|
||
|
* tool read from tty pipe
|
||
|
* cat file | tool read from pipe pipe
|
||
|
*
|
||
|
* lseek() or fseek() on a pipe will fail. Seeking on a tty is supposed
|
||
|
* to fail, but doesn't on some versions of UNIX.
|
||
|
*
|
||
|
* isatty() reports whether an fd is a tty device.
|
||
|
*/
|
||
|
|
||
|
#define IOTYPE(fd) \
|
||
|
((((lseek( (fd), 0, L_INCR )==-1) && (errno==ESPIPE))||isatty( (fd) ))?\
|
||
|
(IMFILEIOFD | IMFILEIOPIPE) \
|
||
|
: \
|
||
|
(IMFILEIOFD | IMFILEIOFILE) \
|
||
|
)
|
||
|
|
||
|
#define FIOTYPE(fp) \
|
||
|
(((fseek( (fp), 0, F_INCR )==-1)||isatty( fileno(fp) ))? \
|
||
|
(IMFILEIOFP | IMFILEIOPIPE) \
|
||
|
: \
|
||
|
(IMFILEIOFP | IMFILEIOFILE) \
|
||
|
)
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileFindFormat - find the format info given a format name
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* The format's name is converted to lower case, then looked up in the
|
||
|
* format table. A pointer to the found format table entry is returned.
|
||
|
*/
|
||
|
|
||
|
ImFileFormat ** /* Returns ptr to file format info*/
|
||
|
#ifdef __STDC__
|
||
|
ImFileFindFormat( char *format )
|
||
|
#else
|
||
|
ImFileFindFormat( format )
|
||
|
char *format; /* Format name to look up */
|
||
|
#endif
|
||
|
{
|
||
|
ImFileFormat **pFormat; /* Pointer into format table */
|
||
|
char **pName; /* Name list pointer */
|
||
|
char lcFormat[IMMAXNAME];/* Lower case name */
|
||
|
char *s; /* String pointer */
|
||
|
|
||
|
if (format==NULL || *format==NULL)
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Convert to lower case. */
|
||
|
for ( s = lcFormat; *format; s++, format++ )
|
||
|
{
|
||
|
if ( isupper( *format ) )
|
||
|
*s = tolower( *format );
|
||
|
else
|
||
|
*s = *format;
|
||
|
}
|
||
|
*s = '\0';
|
||
|
|
||
|
/* Scan the format table. */
|
||
|
for ( pFormat = ImGetFileFormats(); *pFormat; pFormat++ )
|
||
|
{
|
||
|
for ( pName = (*pFormat)->format_names; *pName; pName++ )
|
||
|
if ( strcmp( *pName, lcFormat ) == 0 )
|
||
|
break;
|
||
|
if ( *pName )
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( ( pFormat== NULL ) || (!(*pName)) )
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return ( NULL );
|
||
|
}
|
||
|
return ( pFormat );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileQFormat - query the format of a file
|
||
|
* ImFileQFFormat - query the format of a file
|
||
|
* imFileQFormat - do the actual format search
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* ImFileQFormat( ) and ImFileQFFormat( ) call the underlying
|
||
|
* imFileQFormat( ) to do the actual work, telling it what type of
|
||
|
* file is open and how it was opened (descriptor or pointer).
|
||
|
*
|
||
|
* If input isn't a pipe, tty, or write-only file, we check the magic
|
||
|
* number against the format list. If that doesn't work, we check the
|
||
|
* file name extension against the format list. The name of the format,
|
||
|
* or NULL is returned.
|
||
|
*/
|
||
|
|
||
|
char * /* Returns format name */
|
||
|
#ifdef __STDC__
|
||
|
ImFileQFormat( int fd, char *fileName )
|
||
|
#else
|
||
|
ImFileQFormat( fd, fileName )
|
||
|
int fd; /* File's descriptor */
|
||
|
char *fileName; /* File's name */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileQFormat( /*IOTYPE(fd)*/ 0, fd, NULL, fileName ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
char * /* Returns format name */
|
||
|
#ifdef __STDC__
|
||
|
ImFileQFFormat( FILE *fp, char *fileName )
|
||
|
#else
|
||
|
ImFileQFFormat( fp, fileName )
|
||
|
FILE *fp; /* File's pointer */
|
||
|
char *fileName; /* File's name */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileQFormat( /*FIOTYPE(fp)*/ 0, -1, fp, fileName ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
static char * /* Returns format name */
|
||
|
#ifdef __STDC__
|
||
|
imFileQFormat( int ioType, int fd, FILE *fp, char *fileName )
|
||
|
#else
|
||
|
imFileQFormat( ioType, fd, fp, fileName )
|
||
|
int ioType; /* File descriptor or pointer */
|
||
|
int fd; /* File's descriptor */
|
||
|
FILE *fp; /* File's pointer */
|
||
|
char *fileName; /* File's name */
|
||
|
#endif
|
||
|
{
|
||
|
ImFileFormat **pFmt; /* Format list pointer */
|
||
|
int length; /* Length of magic # */
|
||
|
unsigned char magic1[IMMAXMAGIC];/* Test magic # */
|
||
|
unsigned char magic2[IMMAXMAGIC];/* Another test magic # */
|
||
|
unsigned char *magic; /* Current magic # */
|
||
|
unsigned char *fmtMagic; /* Current format's magic # */
|
||
|
int i; /* Counter */
|
||
|
int doMagic = TRUE; /* Do we check magic numbers? */
|
||
|
char *extension; /* File name extension */
|
||
|
int ioOperation; /* Read or write? */
|
||
|
ImFileMagic *pMagic; /* pointer to magic file info */
|
||
|
char tmpExtension[128];/* Holds the extension */
|
||
|
char *tmp; /* points to a char* */
|
||
|
|
||
|
/*
|
||
|
* Should we check the magic number?
|
||
|
* 1. If file is a pipe or a tty, NO.
|
||
|
* 2. If file is open for writing only, NO.
|
||
|
* 3. Otherwise, YES.
|
||
|
*/
|
||
|
/* if ( ioType & IMFILEIOPIPE )
|
||
|
doMagic = FALSE; // Input is a pipe or a tty
|
||
|
else
|
||
|
{
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
ioOperation = fcntl( fd, F_GETFL, &ioOperation );
|
||
|
else
|
||
|
ioOperation = fcntl( fileno(fp), F_GETFL, &ioOperation);
|
||
|
if ( ioOperation == -1 )
|
||
|
{
|
||
|
ImErrNo = IMESYS; // Bad fp/fd?
|
||
|
return ( NULL );
|
||
|
}
|
||
|
if ( ioOperation == O_WRONLY )
|
||
|
doMagic = FALSE;
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Get the magic number from the file and check it against each of
|
||
|
* the file format list.
|
||
|
*/
|
||
|
if ( doMagic )
|
||
|
{
|
||
|
/*
|
||
|
* Read in the magic number at location 0 (where most are).
|
||
|
*/
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
IMREADMAGIC( fd, magic1, IMMAXMAGIC, 0 );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IMREADFMAGIC( fp, magic1, IMMAXMAGIC, 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Check it against the format list. If a format doesn't
|
||
|
* have a magic number, skip it. If it has a magic number,
|
||
|
* but not at byte 0 in the file, then read in and test
|
||
|
* with that magic. Otherwise test with the magic numbers
|
||
|
* read in above.
|
||
|
*/
|
||
|
magic = magic1;
|
||
|
for ( pFmt = ImGetFileFormats(); *pFmt; pFmt++ )
|
||
|
{
|
||
|
pMagic = (*pFmt)->format_magicMap;
|
||
|
if ( pMagic == NULL )
|
||
|
{
|
||
|
/* This format has no magic numbers. */
|
||
|
continue;
|
||
|
}
|
||
|
for ( ; pMagic->format_magicNumber != NULL; pMagic++ )
|
||
|
{
|
||
|
if ( pMagic->format_magicLength == 0 )
|
||
|
continue; /* No magic # */
|
||
|
if ( pMagic->format_magicLocation != 0 )
|
||
|
{
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
IMREADMAGIC( fd, magic2,
|
||
|
pMagic->format_magicLength,
|
||
|
pMagic->format_magicLocation );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IMREADFMAGIC( fp, magic2,
|
||
|
pMagic->format_magicLength,
|
||
|
pMagic->format_magicLocation );
|
||
|
}
|
||
|
magic = magic2;
|
||
|
}
|
||
|
length = pMagic->format_magicLength;
|
||
|
fmtMagic = pMagic->format_magicNumber;
|
||
|
|
||
|
/* See if it matches the magic number */
|
||
|
for ( i = 0; i < length; i++ )
|
||
|
if ( fmtMagic[i] != magic[i] )
|
||
|
break;
|
||
|
if ( i == length )
|
||
|
return ( (*pFmt)->format_names[0] );
|
||
|
magic = magic1;
|
||
|
}
|
||
|
}
|
||
|
/* No match. Fall through to the file name check. */
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get the file name extension, then check it against the format list.
|
||
|
*/
|
||
|
extension = &fileName[strlen(fileName)-1];
|
||
|
while ( extension != fileName && *extension != '.' )
|
||
|
extension--;
|
||
|
if ( *extension != '.' )
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return ( NULL );
|
||
|
}
|
||
|
strcpy (tmpExtension, extension+1);
|
||
|
|
||
|
pFmt = ImFileFindFormat( tmpExtension );
|
||
|
if ( pFmt != NULL )
|
||
|
return ( (*pFmt)->format_names[0] );
|
||
|
|
||
|
/*
|
||
|
* There may be a compression suffix on the file name.
|
||
|
* Search back to one more '.'.
|
||
|
*/
|
||
|
extension--;
|
||
|
while ( extension != fileName && *extension != '.' )
|
||
|
extension--;
|
||
|
if ( *extension != '.' )
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return ( NULL );
|
||
|
}
|
||
|
strcpy(tmpExtension, extension+1);
|
||
|
|
||
|
/* Take the compression suffix off */
|
||
|
tmp = strchr(tmpExtension,'.');
|
||
|
*tmp = '\0';
|
||
|
|
||
|
pFmt = ImFileFindFormat( tmpExtension );
|
||
|
|
||
|
if (pFmt==NULL)
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return ( NULL );
|
||
|
}
|
||
|
|
||
|
return ( (*pFmt)->format_names[0] );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileQNFormat - query # of formats
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* On the first call, the number of formats in the format table is
|
||
|
* counted and saved away. On all subsequent calls, this saved
|
||
|
* count is returned.
|
||
|
*/
|
||
|
|
||
|
int /* Retursn # of formats */
|
||
|
#ifdef __STDC__
|
||
|
ImFileQNFormat( void )
|
||
|
#else
|
||
|
ImFileQNFormat( )
|
||
|
#endif
|
||
|
{
|
||
|
ImFileFormat **pFmt; /* File format list pointer */
|
||
|
static int count = -1; /* Saved format count */
|
||
|
|
||
|
if ( count != -1 )
|
||
|
return ( count );
|
||
|
for ( count = 0, pFmt = ImGetFileFormats(); *pFmt; count++, pFmt++ )
|
||
|
;
|
||
|
return ( count );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imFileErrorTags - process error tags
|
||
|
* imFileImageTags - process image tags
|
||
|
* imFileInfoTags - process info tags
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* imFileErrorTags looks for the generic error handling tags in the
|
||
|
* flagsTable and sets globals accordingly.
|
||
|
*
|
||
|
* imFileImageTags looks for the image write handling tags in the
|
||
|
* flagsTable and sets arguments accordingly.
|
||
|
*
|
||
|
* imFileInfoTags looks for the generic info handling tags in the
|
||
|
* flagsTable and sets globals accordingly.
|
||
|
*/
|
||
|
|
||
|
static void /* Returns nothing */
|
||
|
#ifdef __STDC__
|
||
|
imFileErrorTags( TagTable *flagsTable )
|
||
|
#else
|
||
|
imFileErrorTags( flagsTable )
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
#endif
|
||
|
{
|
||
|
TagEntry *tmpEntry; /* Tmp table entry holder */
|
||
|
|
||
|
ImErrorProgramName = "program";
|
||
|
ImErrorFileName = "stream";
|
||
|
ImErrorHandler = NULL;
|
||
|
ImErrorStream = NULL;
|
||
|
|
||
|
if ( flagsTable == TAGTABLENULL )
|
||
|
return;
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "program name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImErrorProgramName );
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImErrorFileName );
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "error handler", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImErrorHandler );
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "error stream", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImErrorStream );
|
||
|
}
|
||
|
|
||
|
static void /* Returns nothing */
|
||
|
#ifdef __STDC__
|
||
|
imFileImageTags( TagTable *flagsTable, int *monoThreshold, int *typeRequest,
|
||
|
int *channelRequest, int *depthRequest, int *interRequest,
|
||
|
int *groupRequest, int * qualityRequest, int *compRequest,
|
||
|
int *cltRequest, int *alphaRequest )
|
||
|
#else
|
||
|
imFileImageTags( flagsTable, monoThreshold, typeRequest, channelRequest,
|
||
|
depthRequest, interRequest, groupRequest, qualityRequest, compRequest,
|
||
|
cltRequest, alphaRequest )
|
||
|
TagTable *flagsTable; /* User flags */
|
||
|
int *monoThreshold; /* Mono conversion threshold */
|
||
|
int *typeRequest; /* Image type request */
|
||
|
int *channelRequest; /* # of channels requested */
|
||
|
int *depthRequest; /* Depth request */
|
||
|
int *interRequest; /* Interleave request */
|
||
|
int *groupRequest; /* Group request */
|
||
|
int *qualityRequest; /* Quality request */
|
||
|
int *compRequest; /* Compression request */
|
||
|
int *cltRequest; /* CLT request */
|
||
|
int *alphaRequest; /* Alpha request */
|
||
|
#endif
|
||
|
{
|
||
|
TagEntry *tmpEntry; /* Table entry holder */
|
||
|
|
||
|
*monoThreshold = IMDEFMONOTHRESH;
|
||
|
*typeRequest = -1;
|
||
|
*channelRequest = -1;
|
||
|
*depthRequest = -1;
|
||
|
*interRequest = -1;
|
||
|
*compRequest = -1;
|
||
|
*cltRequest = -1;
|
||
|
*alphaRequest = -1;
|
||
|
*groupRequest = -1;
|
||
|
*qualityRequest = -1;
|
||
|
|
||
|
if ( flagsTable == TAGTABLENULL )
|
||
|
return;
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image mono threshold", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, monoThreshold );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image interleave request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, interRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image type request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, typeRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image channel number request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, channelRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image channel depth request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, depthRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image compression request", 0);
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, compRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image clt request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, cltRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image alpha request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, alphaRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image group request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, groupRequest );
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "image compression quality request", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
{
|
||
|
/* We don't care about the value; we just care that the
|
||
|
request is there. */
|
||
|
*qualityRequest = IMQUALITYYES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void /* Returns nothing */
|
||
|
#ifdef __STDC__
|
||
|
imFileInfoTags( TagTable *flagsTable )
|
||
|
#else
|
||
|
imFileInfoTags( flagsTable )
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
#endif
|
||
|
{
|
||
|
TagEntry *tmpEntry; /* Tmp table entry holder */
|
||
|
|
||
|
ImInfoHandler = NULL;
|
||
|
ImInfoStream = NULL;
|
||
|
|
||
|
if ( flagsTable == TAGTABLENULL )
|
||
|
return;
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "info handler", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImInfoHandler );
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "info stream", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagEntryQValue( tmpEntry, &ImInfoStream );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileRead - read an image file from a file descriptor
|
||
|
* ImFileFRead - read an image file from a file pointer
|
||
|
* imFileRead - do the real work of read
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* ImFileRead and ImFileFRead just call imFileRead to do their work.
|
||
|
*
|
||
|
* Incomming arguments are checked and the flagsTable scanned for
|
||
|
* relevant flags. The format table is searched for the format's
|
||
|
* read function, and that function called.
|
||
|
*/
|
||
|
|
||
|
int /* Returns number of entries added*/
|
||
|
#ifdef __STDC__
|
||
|
ImFileRead( int fd, char *format, TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
ImFileRead( fd, format, flagsTable, tagTable )
|
||
|
int fd; /* File descriptor to read from */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to append to */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileRead( /*IOTYPE(fd)*/ 0, fd, NULL, format,
|
||
|
flagsTable, tagTable ) );
|
||
|
}
|
||
|
|
||
|
int /* Returns number of entries added*/
|
||
|
#ifdef __STDC__
|
||
|
ImFileFRead( FILE *fp, char *format, TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
ImFileFRead( fp, format, flagsTable, tagTable )
|
||
|
FILE *fp; /* File pointer to read from */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to append to */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileRead( FIOTYPE(fp), -1, fp, format,
|
||
|
flagsTable, tagTable ) );
|
||
|
}
|
||
|
|
||
|
static int /* Returns status */
|
||
|
#ifdef __STDC__
|
||
|
imFileRead( int ioType, int fd, FILE *fp, char *format, TagTable *flagsTable,
|
||
|
TagTable *tagTable )
|
||
|
#else
|
||
|
imFileRead( ioType, fd, fp, format, flagsTable, tagTable )
|
||
|
int ioType; /* I/O flags */
|
||
|
int fd; /* Input file descriptor */
|
||
|
FILE *fp; /* Input file pointer */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to append to */
|
||
|
#endif
|
||
|
{
|
||
|
ImFileFormat **pFormat; /* Pointer into format table */
|
||
|
FILE *tmpFp; /* Temporary fp */
|
||
|
int tmpFd; /* Temporary fd */
|
||
|
char tmpName[1024]; /* Temporary file name */
|
||
|
unsigned char buffer[1024]; /* Temporary write buffer */
|
||
|
char message[1024]; /* Holds iminfo message */
|
||
|
int status; /* Return status */
|
||
|
int n; /* # of bytes read */
|
||
|
int binByteOrder; /* Current byte order */
|
||
|
int binFloatFormat; /* Current floating point format*/
|
||
|
TagEntry *tmpEntry; /* Tmp table entry holder */
|
||
|
char *fileName; /* name of file */
|
||
|
char *tmp; /* temp string */
|
||
|
ImCompressScheme* pCompress; /* compression scheme */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get and save the current state of the Binary I/O package.
|
||
|
*/
|
||
|
binByteOrder = BinQByteOrder( );
|
||
|
binFloatFormat = BinQFloatFormat( );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get stuff from the flags table.
|
||
|
*/
|
||
|
imFileErrorTags( flagsTable );
|
||
|
imFileInfoTags( flagsTable );
|
||
|
|
||
|
/* Check tagTable */
|
||
|
if ( tagTable == TAGTABLENULL )
|
||
|
{
|
||
|
ImErrNo = IMENULLTAGTABLE;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check for compression.
|
||
|
*/
|
||
|
if ((tmp = imFileQCompression(ioType, fd, fp, flagsTable))!=NULL)
|
||
|
{
|
||
|
pCompress = ImFileFindCompressionScheme(tmp);
|
||
|
sprintf(message,"'%s' (%s)",pCompress->compress_suffixes[0],pCompress->compress_name);
|
||
|
ImInfo("File Compression",message);
|
||
|
if (imFileReadCompressedFile(ioType, fd, fp, flagsTable, tagTable, tmp)==-1)
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* Read an uncompressed file
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* If no format was specifed, get the format from the
|
||
|
* file name, or flags in the flags table.
|
||
|
*/
|
||
|
if (format==NULL || *format=='\0')
|
||
|
{
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
{
|
||
|
TagEntryQValue( tmpEntry, &fileName);
|
||
|
}
|
||
|
else
|
||
|
fileName = NULL;
|
||
|
if (ioType & IMFILEIOFD)
|
||
|
format = ImFileQFormat(fd, fileName);
|
||
|
else
|
||
|
format = ImFileQFFormat(fp, fileName);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Look up the format
|
||
|
*/
|
||
|
if ( (pFormat = ImFileFindFormat( format )) == NULL )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
if ( (*pFormat)->format_read == NULL )
|
||
|
{
|
||
|
ImErrNo = IMENOREAD;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help);
|
||
|
ImInfo("Image Format",message);
|
||
|
|
||
|
/*
|
||
|
* If the fd/fp is a file, or it's a pipe and the handler can read
|
||
|
* directly to a pipe, read it.
|
||
|
*/
|
||
|
if ( ioType & IMFILEIOFILE || (*pFormat)->format_readPipe == IMPIPE )
|
||
|
{
|
||
|
status = (*(*pFormat)->format_read)( ioType, fd, fp,
|
||
|
flagsTable, tagTable );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* Create a temporary file, copy input to the temp file, then do
|
||
|
* all reading from it. When complete, delete the file, then
|
||
|
* return. If errors occur, save them until the unlink has been done.
|
||
|
*/
|
||
|
strcpy( tmpName, imMakeTempFile() );
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
while ( (n = read( fd, buffer, sizeof( buffer ) )) > 0 )
|
||
|
write( tmpFd, buffer, n );
|
||
|
lseek( tmpFd, 0, 0 );
|
||
|
status = (*(*pFormat)->format_read)( IMFILEIOFD | IMFILEIOFILE,
|
||
|
tmpFd, NULL, flagsTable, tagTable );
|
||
|
close( tmpFd );
|
||
|
unlink( tmpName );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
while ( (n = fread( buffer, 1, sizeof( buffer ), fp )) != 0 )
|
||
|
fwrite( buffer, 1, n, tmpFp );
|
||
|
fseek( tmpFp, 0, 0 );
|
||
|
status = (*(*pFormat)->format_read)( IMFILEIOFP | IMFILEIOFILE, -1,
|
||
|
tmpFp, flagsTable, tagTable );
|
||
|
fclose( tmpFp );
|
||
|
unlink( tmpName );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* Restore the Binary I/O library state.
|
||
|
*/
|
||
|
BinByteOrder( binByteOrder );
|
||
|
BinFloatFormat( binFloatFormat );
|
||
|
|
||
|
/*
|
||
|
* Process input requests
|
||
|
*/
|
||
|
if ( flagsTable && TagTableQDirect(flagsTable, "channel map request", 0) != TAGENTRYNULL )
|
||
|
{
|
||
|
if (ImVfbProcessMapRequests( flagsTable, tagTable )<=0)
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imFileSelectMap - select a good write map to use
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* The format's write map is scanned to find the best match for output
|
||
|
* of the given VFB based upon the user's requests.
|
||
|
*/
|
||
|
|
||
|
static ImFileFormatWriteMap * /* Returns write map entry */
|
||
|
#ifdef __STDC__
|
||
|
imFileSelectMap( ImVfb *pVfb, ImFileFormat **pFormat, int typeRequest,
|
||
|
int channelRequest, int depthRequest, int interRequest,
|
||
|
int groupRequest, int qualityRequest, int otherRequest, int otherRequestMask )
|
||
|
#else
|
||
|
imFileSelectMap( pVfb, pFormat, typeRequest, channelRequest, depthRequest,
|
||
|
interRequest, groupRequest, qualityRequest, otherRequest, otherRequestMask )
|
||
|
ImVfb *pVfb; /* Image to handle */
|
||
|
ImFileFormat **pFormat; /* Format conversion info */
|
||
|
int typeRequest; /* Type request */
|
||
|
int channelRequest; /* Channel request */
|
||
|
int depthRequest; /* Depth request */
|
||
|
int interRequest; /* Interleave request */
|
||
|
int groupRequest; /* Group request */
|
||
|
int qualityRequest; /* Quality request */
|
||
|
int otherRequest; /* CLT/Alpha/Comp requests */
|
||
|
int otherRequestMask; /* Which of those are in otherRequest*/
|
||
|
#endif
|
||
|
{
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry */
|
||
|
int fields; /* VFB field mask */
|
||
|
imFilePrefTable *pPrefTable; /* Preference table */
|
||
|
imFilePref *pPrefSubTable; /* Preference subtable */
|
||
|
imFilePref *pPref; /* Preference table entry ptr */
|
||
|
int bestIndex; /* Best preference index */
|
||
|
ImFileFormatWriteMap *pBest; /* Best write map entry */
|
||
|
int i; /* Counters */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* To gauge which of possibly many output image types (and their
|
||
|
* CLT, alpha, interleave, grouping, and compression attributes) is
|
||
|
* "best" for the incomming VFB we use a series of "preference tables".
|
||
|
* There is one master table for each VFB type (mono, index8,
|
||
|
* index16, and rgb). Within each table are four sub-tables, for
|
||
|
* each combination of (have/don't have CLT) and (have/don't have
|
||
|
* alpha planes).
|
||
|
*
|
||
|
* Within each sub-table are 16 entries for every combination of
|
||
|
* (mono, index8, index16, rgb), (have/don't have CLT), and
|
||
|
* (have/don't have alpha). These are ordered from most preferable
|
||
|
* to least preferable for this VFB.
|
||
|
*
|
||
|
* In evaluating a given format's selection of write map entries,
|
||
|
* we look up each one in the preference table and get the table
|
||
|
* index of it's entry. The entry with the lowest index, of those
|
||
|
* available to the format, is the one we select.
|
||
|
*
|
||
|
* Among several write map entries with the same preference value
|
||
|
* (same incomming VFB and attributes but different channel numbers
|
||
|
* and depths), the earliest one in the write map is chosen.
|
||
|
*/
|
||
|
fields = ImVfbQFields( pVfb );
|
||
|
if ( fields & IMVFBMONO )
|
||
|
pPrefTable = &imPrefMono;
|
||
|
else if ( fields & IMVFBINDEX8 )
|
||
|
pPrefTable = &imPrefIndex8;
|
||
|
else if ( fields & IMVFBINDEX16 )
|
||
|
pPrefTable = &imPrefIndex16;
|
||
|
else if ( fields & IMVFBRGB )
|
||
|
pPrefTable = &imPrefRgb;
|
||
|
else
|
||
|
{
|
||
|
/* What kind of image is stored here? */
|
||
|
return ( NULL );
|
||
|
}
|
||
|
if ( fields & IMVFBALPHA )
|
||
|
{
|
||
|
if ( ImVfbQClt( pVfb ) != IMCLTNULL )
|
||
|
pPrefSubTable = pPrefTable->pref_cltAlpha;
|
||
|
else
|
||
|
pPrefSubTable = pPrefTable->pref_noCltAlpha;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( ImVfbQClt( pVfb ) != IMCLTNULL )
|
||
|
pPrefSubTable = pPrefTable->pref_cltNoAlpha;
|
||
|
else
|
||
|
pPrefSubTable = pPrefTable->pref_noCltNoAlpha;
|
||
|
}
|
||
|
|
||
|
bestIndex = 1000;
|
||
|
pBest = NULL;
|
||
|
for ( pMap = (*pFormat)->format_writeMap; pMap->map_inField != -1; pMap++ )
|
||
|
{
|
||
|
/* Reject if not the type, # channel, channel depth requested.*/
|
||
|
if ( typeRequest != -1 && pMap->map_outType != typeRequest )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if ( channelRequest != -1 && pMap->map_outNChannels != channelRequest )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
if ( depthRequest != -1 && pMap->map_outChannelDepth != depthRequest )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Reject if not the CLT/Alpha/Comp requested. */
|
||
|
if ( (pMap->map_outAttributes & otherRequestMask) !=
|
||
|
otherRequest )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Reject if not the interleave requested. */
|
||
|
if ( (interRequest != -1) &&
|
||
|
((pMap->map_outAttributes & IMINTERMASK) != interRequest) )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Reject if not the group request */
|
||
|
if ( (groupRequest != -1) &&
|
||
|
((pMap->map_outAttributes & IMGROUPMASK) != groupRequest))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Reject if not the quality request */
|
||
|
if ( (qualityRequest != -1) &&
|
||
|
((pMap->map_outAttributes & IMQUALITYMASK) != qualityRequest))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/* Find it's preference index. */
|
||
|
for ( pPref = pPrefSubTable, i = 0; i < 16; i++, pPref++ )
|
||
|
{
|
||
|
if ( pMap->map_inField != pPref->pref_field ||
|
||
|
pMap->map_inAttributes !=pPref->pref_attributes)
|
||
|
continue;
|
||
|
|
||
|
/* Match. Better than last match? */
|
||
|
if ( i < bestIndex )
|
||
|
{
|
||
|
bestIndex = i;
|
||
|
pBest = pMap;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( pBest == NULL )
|
||
|
{
|
||
|
/* Nothing got past user requirements! */
|
||
|
return ( NULL );
|
||
|
}
|
||
|
return ( pBest );
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileWrite - write an image file to a file descriptor
|
||
|
* ImFileFWrite - write an image file to a file pointer
|
||
|
* imFileWrite - do the real work of write
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* ImFileWRite and ImFileFWRite just call imFileWRite to do their work.
|
||
|
*
|
||
|
* Incoming arguments are checked and the flagsTable scanned for
|
||
|
* relevant flags. The format table is searched for the format's
|
||
|
* write map. The write map is scanned to find the best match, or
|
||
|
* the match requested by incomming flags. If the match requires
|
||
|
* depth conversion of the VFB(s), it's done. The match's write
|
||
|
* routine is then called.
|
||
|
*
|
||
|
* If NULL is passed as the image file format, then the format is
|
||
|
* discerned from this routine.
|
||
|
*
|
||
|
* If the format parameter points to NULL, then the format is discerned
|
||
|
* here, AND the format which is ultimately chosen is returned in the format
|
||
|
* parameter.
|
||
|
*/
|
||
|
|
||
|
int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
ImFileWrite( int fd, char *format, TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
ImFileWrite( fd, format, flagsTable, tagTable )
|
||
|
int fd; /* Output file descriptor */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileWrite( IOTYPE(fd), fd, NULL, format,
|
||
|
flagsTable, tagTable ) );
|
||
|
}
|
||
|
|
||
|
int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
ImFileFWrite( FILE *fp, char *format, TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
ImFileFWrite( fp, format, flagsTable, tagTable )
|
||
|
FILE *fp; /* Output file pointer */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
return ( imFileWrite( FIOTYPE(fp), -1, fp, format,
|
||
|
flagsTable, tagTable ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
imFileWrite( int ioType, int fd, FILE *fp, char *format, TagTable *flagsTable,
|
||
|
TagTable *tagTable )
|
||
|
#else
|
||
|
imFileWrite( ioType, fd, fp, format, flagsTable, tagTable )
|
||
|
int ioType; /* I/O flags */
|
||
|
int fd; /* Output file descriptor */
|
||
|
FILE *fp; /* Output file pointer */
|
||
|
char *format; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
int nVfb; /* # of vfb's in tag table */
|
||
|
ImFileFormat **pFormat; /* Pointer into format table */
|
||
|
int status; /* Return status */
|
||
|
int binByteOrder; /* Current byte order */
|
||
|
int binFloatFormat; /* Current floating point format*/
|
||
|
char* fileName; /* name of file (in flagsTable) */
|
||
|
TagEntry* tmpEntry; /* entry in tag table */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get and save the current state of the Binary I/O package.
|
||
|
*/
|
||
|
binByteOrder = BinQByteOrder( );
|
||
|
binFloatFormat = BinQFloatFormat( );
|
||
|
|
||
|
/* Check integrity of tagTable */
|
||
|
if ( tagTable == TAGTABLENULL )
|
||
|
{
|
||
|
ImErrNo = IMENULLTAGTABLE;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
nVfb = TagTableQNEntry( tagTable, "image vfb" );
|
||
|
if ( nVfb < 1 )
|
||
|
{
|
||
|
ImErrNo = IMENOVFB;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get stuff from the flags table.
|
||
|
*/
|
||
|
imFileErrorTags( flagsTable );
|
||
|
imFileInfoTags( flagsTable );
|
||
|
|
||
|
/*
|
||
|
* If no format was specifed, get the format from the
|
||
|
* file name, or flags in the flags table.
|
||
|
*/
|
||
|
if (format==NULL || *format=='\0')
|
||
|
{
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
{
|
||
|
TagEntryQValue( tmpEntry, &fileName);
|
||
|
}
|
||
|
else
|
||
|
fileName = NULL;
|
||
|
/*
|
||
|
* Return the format name in 'format' if
|
||
|
* NULL was not passed
|
||
|
*/
|
||
|
if (format==NULL)
|
||
|
{
|
||
|
if (ioType & IMFILEIOFD)
|
||
|
format = ImFileQFormat(fd, fileName);
|
||
|
else
|
||
|
format = ImFileQFFormat(fp, fileName);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (ioType & IMFILEIOFD)
|
||
|
strcpy(format,ImFileQFormat(fd, fileName));
|
||
|
else
|
||
|
strcpy(format, ImFileQFFormat(fp, fileName));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Handle any channel mapping requests
|
||
|
*/
|
||
|
if ( flagsTable && TagTableQDirect(flagsTable, "channel map request", 0)
|
||
|
!= TAGENTRYNULL )
|
||
|
{
|
||
|
if (ImVfbProcessMapRequests( flagsTable, tagTable )<=0)
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Find the format
|
||
|
*/
|
||
|
if ( (pFormat = ImFileFindFormat( format )) == NULL )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
if ( (*pFormat)->format_writeMap == NULL )
|
||
|
{
|
||
|
ImErrNo = IMENOWRITE;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Handle the single- and multiple-VFB cases separately.
|
||
|
*/
|
||
|
if ( nVfb == 1 )
|
||
|
status = imFileWriteSingle( ioType, fd, fp,
|
||
|
pFormat, flagsTable, tagTable );
|
||
|
else
|
||
|
status = imFileWriteMultiple( ioType, fd, fp,
|
||
|
pFormat, flagsTable, tagTable );
|
||
|
|
||
|
/*
|
||
|
* Restore the Binary I/O library state.
|
||
|
*/
|
||
|
BinByteOrder( binByteOrder );
|
||
|
BinFloatFormat( binFloatFormat );
|
||
|
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
imFileWriteSingle( int ioType, int fd, FILE *fp, ImFileFormat **pFormat,
|
||
|
TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
imFileWriteSingle( ioType, fd, fp, pFormat, flagsTable, tagTable )
|
||
|
int ioType; /* I/O flags */
|
||
|
int fd; /* Output file descriptor */
|
||
|
FILE *fp; /* Output file pointer */
|
||
|
ImFileFormat **pFormat; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
TagEntry *tmpEntry; /* Tmp table entry holder */
|
||
|
TagEntry *newEntry; /* New table entry holder */
|
||
|
TagTable *newTable; /* New Tag Table */
|
||
|
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry */
|
||
|
|
||
|
ImVfb *origVfb; /* Original VFB */
|
||
|
int origFields; /* Original VFB field mask */
|
||
|
ImVfb *newVfb; /* New VFB */
|
||
|
ImVfb *statusVfb; /* Return status as a VFB */
|
||
|
int newFields; /* New VFB field mask */
|
||
|
ImClt *newClt; /* New CLT */
|
||
|
|
||
|
int status; /* Return status */
|
||
|
int nEntry; /* Tag entry # */
|
||
|
|
||
|
int monoThreshold; /* Mono conversion threshold */
|
||
|
int typeRequest; /* Type request */
|
||
|
int channelRequest; /* Channel request */
|
||
|
int depthRequest; /* Depth request */
|
||
|
int interRequest; /* Interleave request */
|
||
|
int groupRequest; /* Group request */
|
||
|
int qualityRequest; /* Quality request */
|
||
|
int compRequest; /* Compression request */
|
||
|
int cltRequest; /* CLT request */
|
||
|
int alphaRequest; /* Alpha request */
|
||
|
int otherRequest; /* CLT/Alpha/Comp requests */
|
||
|
int otherRequestMask; /* Which of those are in otherRequest*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get stuff from the flags table.
|
||
|
*/
|
||
|
imFileImageTags( flagsTable, &monoThreshold, &typeRequest,
|
||
|
&channelRequest, &depthRequest, &interRequest,
|
||
|
&groupRequest, &qualityRequest, &compRequest, &cltRequest,
|
||
|
&alphaRequest );
|
||
|
otherRequest = 0;
|
||
|
otherRequestMask = 0;
|
||
|
if ( compRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (compRequest & IMCOMPMASK);
|
||
|
otherRequestMask |= IMCOMPMASK;
|
||
|
}
|
||
|
if ( cltRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (cltRequest & IMCLTMASK);
|
||
|
otherRequestMask |= IMCLTMASK;
|
||
|
}
|
||
|
if ( alphaRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (alphaRequest & IMALPHAMASK);
|
||
|
otherRequestMask |= IMALPHAMASK;
|
||
|
}
|
||
|
if ( groupRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (groupRequest & IMGROUPMASK);
|
||
|
otherRequestMask |= IMGROUPMASK;
|
||
|
}
|
||
|
if ( qualityRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (qualityRequest & IMQUALITYMASK);
|
||
|
otherRequestMask |= IMQUALITYMASK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get the single VFB in the tag table and determine its type.
|
||
|
*/
|
||
|
TagEntryQValue( TagTableQDirect( tagTable, "image vfb", 0 ), &origVfb );
|
||
|
origFields = ImVfbQFields( origVfb );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get the write map entry that matches this VFB best.
|
||
|
*/
|
||
|
pMap = imFileSelectMap( origVfb, pFormat, typeRequest, channelRequest,
|
||
|
depthRequest, interRequest, groupRequest, qualityRequest, otherRequest,
|
||
|
otherRequestMask );
|
||
|
if ( pMap == NULL )
|
||
|
{
|
||
|
/* Can't do it. */
|
||
|
ImErrNo = IMENOTPOSSIBLE;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Avoid making a new VFB and new tag table if the current
|
||
|
* one matches in depth and doesn't require an alpha plane.
|
||
|
*/
|
||
|
if ( (origFields & pMap->map_inField) &&
|
||
|
!( (pMap->map_inAttributes & IMALPHAYES) &&
|
||
|
!(origFields & IMVFBALPHA) ) )
|
||
|
{
|
||
|
/*
|
||
|
* Generate a CLT if we need to.
|
||
|
*/
|
||
|
if ( (ImVfbQClt( origVfb ) == IMCLTNULL) &&
|
||
|
(pMap->map_inAttributes & IMCLTYES) )
|
||
|
{
|
||
|
/*
|
||
|
* Make a CLT, attach it, write it all out,
|
||
|
* then remove the CLT and throw it away.
|
||
|
*
|
||
|
* Note that we always generate a grayscale CLT:
|
||
|
* if Mono, no CLT means grayscale.
|
||
|
* if Index8, no CLT must default to grayscale.
|
||
|
* if Index16, no CLT must default to grayscale.
|
||
|
* if RGB, any CLT is meaningless. So grayscale.
|
||
|
*/
|
||
|
if ( origFields & IMVFBMONO )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0,
|
||
|
255, IMCLTNEW);
|
||
|
else if ( origFields & IMVFBINDEX16 )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535,
|
||
|
0, 65535, IMCLTNEW);
|
||
|
else
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 255, 0,
|
||
|
255, IMCLTNEW);
|
||
|
if ( newClt == IMCLTNULL )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
ImVfbSClt( origVfb, newClt );
|
||
|
status = imFileWriteIt( pMap,
|
||
|
ioType, fd, fp, pFormat,
|
||
|
flagsTable, tagTable );
|
||
|
ImVfbSClt( origVfb, IMCLTNULL );
|
||
|
ImCltFree( newClt );
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* The VFB matches just fine. Write it out.
|
||
|
*/
|
||
|
return ( imFileWriteIt( pMap, ioType, fd, fp, pFormat,
|
||
|
flagsTable, tagTable ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Allocate a new VFB.
|
||
|
*/
|
||
|
newFields = pMap->map_inField;
|
||
|
if ( pMap->map_inAttributes & IMALPHAYES )
|
||
|
newFields |= IMVFBALPHA;
|
||
|
|
||
|
newVfb = ImVfbAlloc( ImVfbQWidth( origVfb ), ImVfbQHeight( origVfb ),
|
||
|
newFields );
|
||
|
if ( newVfb == IMVFBNULL )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
if ( newFields & IMVFBALPHA )
|
||
|
{
|
||
|
if ( origFields & IMVFBALPHA )
|
||
|
{
|
||
|
/*
|
||
|
* Copy alpha from original VFB.
|
||
|
*/
|
||
|
if ( ImVfbCopy( origVfb, 0, 0, ImVfbQWidth( origVfb ),
|
||
|
ImVfbQHeight( origVfb ), IMVFBALPHA,
|
||
|
newVfb, 0, 0 ) == IMVFBNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* Generate an empty alpha plane.
|
||
|
*/
|
||
|
if ( ImVfbClear( IMVFBALPHA, ~0, newVfb ) == IMVFBNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Copy the original image to the new VFB, changing
|
||
|
* depth if necessary.
|
||
|
*/
|
||
|
if ( newFields & IMVFBMONO )
|
||
|
statusVfb = ImVfbToMono( origVfb, monoThreshold, newVfb );
|
||
|
else if ( newFields & IMVFBINDEX8 )
|
||
|
statusVfb = ImVfbToIndex8( origVfb, newVfb );
|
||
|
else if ( newFields & IMVFBINDEX16 )
|
||
|
statusVfb = ImVfbToIndex16( origVfb, newVfb );
|
||
|
else if ( newFields & IMVFBRGB )
|
||
|
statusVfb = ImVfbToRgb( origVfb, newVfb );
|
||
|
else
|
||
|
{
|
||
|
/* Unknown VFB selection? How is that possible? */
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrNo = IMENOTPOSSIBLE;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
if ( statusVfb == IMVFBNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add a CLT if necessary.
|
||
|
*/
|
||
|
newClt = ImVfbQClt( newVfb );
|
||
|
if ( (newClt == IMCLTNULL) && (pMap->map_inAttributes & IMCLTYES) )
|
||
|
{
|
||
|
/*
|
||
|
* Note that we always generate a grayscale CLT:
|
||
|
* if Mono, no CLT means grayscale.
|
||
|
* if Index8, no CLT must default to grayscale.
|
||
|
* if Index16, no CLT must default to grayscale.
|
||
|
* if RGB, any CLT is meaningless. So grayscale.
|
||
|
* Mono gets a 2 entry CLT. All others 255 entries.
|
||
|
*/
|
||
|
if ( newFields & IMVFBMONO )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0, 255,
|
||
|
IMCLTNEW );
|
||
|
else if ( newFields & IMVFBINDEX16 )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0, 65535,
|
||
|
IMCLTNEW );
|
||
|
else
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 255,0,255,
|
||
|
IMCLTNEW );
|
||
|
if ( newClt == IMCLTNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
ImVfbSClt( newVfb, newClt );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Copy the tag table, replacing the VFB and CLT, if any.
|
||
|
*
|
||
|
* Note: we can't just temporarly swap the new VFB into the
|
||
|
* original tag table. Doing so causes the VFB's tag entry to be
|
||
|
* freed and a new one allocated. If the caller had a pointer to
|
||
|
* that entry, it would become garbage. So, we are not allowed
|
||
|
* to change, in any way, the incomming tag table.
|
||
|
*/
|
||
|
newTable = TagTableAlloc( );
|
||
|
if ( newTable == TAGTABLENULL )
|
||
|
{
|
||
|
ImCltFree( newClt );
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrNo = IMEMALLOC;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
TagTableCopy( tagTable, newTable, 0, TagTableQNEntry( tagTable, NULL )-1 );
|
||
|
|
||
|
/* Replace the VFB. */
|
||
|
tmpEntry = TagTableQDirect( newTable, "image vfb", 0 );
|
||
|
nEntry = TagEntryQNthEntry( tmpEntry );
|
||
|
TagTableReplace( newTable, TagEntryQNthEntry( tmpEntry ),
|
||
|
TagEntryAlloc( "image vfb", POINTER, &newVfb ) );
|
||
|
|
||
|
/* Replace the CLT (assume there is at most one!). */
|
||
|
tmpEntry = TagTableQDirect( newTable, "image clt", 0 );
|
||
|
if ( newClt == IMCLTNULL )
|
||
|
{
|
||
|
/* New format doesn't want CLT. Remove it, if any. */
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagTableDelete( newTable, TagEntryQNthEntry( tmpEntry));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* New format requires CLT. Replace or insert. */
|
||
|
newEntry = TagEntryAlloc( "image clt", POINTER, &newClt);
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
TagTableReplace( newTable, TagEntryQNthEntry( tmpEntry),
|
||
|
newEntry );
|
||
|
else
|
||
|
TagTableInsert( newTable, nEntry - 1, newEntry );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Write it out, then clean up.
|
||
|
*/
|
||
|
status = imFileWriteIt( pMap, ioType, fd, fp, pFormat,
|
||
|
flagsTable, newTable );
|
||
|
|
||
|
if ( newClt != IMCLTNULL )
|
||
|
ImCltFree( newClt );
|
||
|
ImVfbFree( newVfb );
|
||
|
TagTableFree( newTable );
|
||
|
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
static int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
imFileWriteMultiple( int ioType, int fd, FILE *fp, ImFileFormat **pFormat,
|
||
|
TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
imFileWriteMultiple( ioType, fd, fp, pFormat, flagsTable, tagTable )
|
||
|
int ioType; /* I/O flags */
|
||
|
int fd; /* Output file descriptor */
|
||
|
FILE *fp; /* Output file pointer */
|
||
|
ImFileFormat **pFormat; /* File format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
TagEntry *tmpEntry; /* Tmp table entry holder */
|
||
|
TagTable *newTable; /* New Tag Table */
|
||
|
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry */
|
||
|
|
||
|
ImVfb *origVfb; /* Original VFB */
|
||
|
int origFields; /* Original VFB field mask */
|
||
|
ImVfb *newVfb; /* New VFB */
|
||
|
int newFields; /* New VFB field mask */
|
||
|
ImClt *newClt; /* New CLT */
|
||
|
ImVfb *statusVfb; /* Return status as a VFB */
|
||
|
|
||
|
int status; /* Return status */
|
||
|
int nEntry; /* Tag entry # */
|
||
|
int i; /* Entry # */
|
||
|
char *tag; /* Tag name */
|
||
|
int error; /* Error occured? */
|
||
|
|
||
|
int monoThreshold; /* Mono conversion threshold */
|
||
|
int typeRequest; /* Type request */
|
||
|
int channelRequest; /* Channel request */
|
||
|
int depthRequest; /* Depth request */
|
||
|
int interRequest; /* Interleave request */
|
||
|
int groupRequest; /* Group request */
|
||
|
int qualityRequest; /* Quality request */
|
||
|
int compRequest; /* Compression request */
|
||
|
int cltRequest; /* CLT request */
|
||
|
int alphaRequest; /* Alpha request */
|
||
|
int otherRequest; /* CLT/Alpha/Comp requests */
|
||
|
int otherRequestMask; /* Which of those are in otherRequest*/
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get stuff from the flags table.
|
||
|
*/
|
||
|
imFileImageTags( flagsTable, &monoThreshold, &typeRequest,
|
||
|
&channelRequest, &depthRequest, &interRequest,
|
||
|
&groupRequest, & qualityRequest, &compRequest,
|
||
|
&cltRequest, &alphaRequest );
|
||
|
otherRequest = 0;
|
||
|
otherRequestMask = 0;
|
||
|
if ( compRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (compRequest & IMCOMPMASK);
|
||
|
otherRequestMask |= IMCOMPMASK;
|
||
|
}
|
||
|
if ( cltRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (cltRequest & IMCLTMASK);
|
||
|
otherRequestMask |= IMCLTMASK;
|
||
|
}
|
||
|
if ( alphaRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (alphaRequest & IMALPHAMASK);
|
||
|
otherRequestMask |= IMALPHAMASK;
|
||
|
}
|
||
|
if ( groupRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (groupRequest & IMGROUPMASK);
|
||
|
otherRequestMask |= IMGROUPMASK;
|
||
|
}
|
||
|
if ( qualityRequest != -1 )
|
||
|
{
|
||
|
otherRequest |= (qualityRequest & IMQUALITYMASK);
|
||
|
otherRequestMask |= IMQUALITYMASK;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Allocate a new tag table.
|
||
|
*/
|
||
|
newTable = TagTableAlloc( );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Walk through the original tag table, watching for VFB's and CLT's.
|
||
|
* Everything else is copied straight across.
|
||
|
*/
|
||
|
nEntry = TagTableQNEntry( tagTable, NULL );
|
||
|
error = FALSE;
|
||
|
for ( i = 0; i < nEntry; i++ )
|
||
|
{
|
||
|
tmpEntry = TagTableQLinear( tagTable, i );
|
||
|
tag = TagEntryQTag( tmpEntry );
|
||
|
|
||
|
if ( strcmp( tag, "image clt" ) == 0 )
|
||
|
continue; /* Remove these for now */
|
||
|
|
||
|
if ( strcmp( tag, "image vfb" ) != 0 )
|
||
|
{
|
||
|
/* Some other tag. Copy to new table. */
|
||
|
TagTableCopy( tagTable, newTable, i, i );
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* At this point we have a VFB from the original tag table.
|
||
|
*/
|
||
|
TagEntryQValue( tmpEntry, &origVfb );
|
||
|
origFields = ImVfbQFields( origVfb );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Get the write map entry that matches this VFB best.
|
||
|
*/
|
||
|
pMap = imFileSelectMap( origVfb, pFormat, typeRequest,
|
||
|
channelRequest, depthRequest, interRequest,
|
||
|
groupRequest, qualityRequest, otherRequest, otherRequestMask );
|
||
|
if ( pMap == NULL )
|
||
|
{
|
||
|
/* Can't do it. */
|
||
|
ImErrNo = IMENOTPOSSIBLE;
|
||
|
error = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Allocate a new VFB.
|
||
|
*/
|
||
|
newFields = pMap->map_inField;
|
||
|
if ( pMap->map_inAttributes & IMALPHAYES )
|
||
|
newFields |= IMVFBALPHA;
|
||
|
|
||
|
newVfb = ImVfbAlloc( ImVfbQWidth( origVfb ),
|
||
|
ImVfbQHeight( origVfb ), newFields );
|
||
|
if ( newVfb == IMVFBNULL )
|
||
|
{
|
||
|
error = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ( newFields & IMVFBALPHA )
|
||
|
{
|
||
|
if ( origFields & IMVFBALPHA )
|
||
|
{
|
||
|
/*
|
||
|
* Copy alpha from original VFB.
|
||
|
*/
|
||
|
if ( ImVfbCopy( origVfb, 0, 0,
|
||
|
ImVfbQWidth( origVfb ),
|
||
|
ImVfbQHeight( origVfb ), IMVFBALPHA,
|
||
|
newVfb, 0, 0 ) == IMVFBNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/*
|
||
|
* Generate an empty alpha plane.
|
||
|
*/
|
||
|
if ( ImVfbClear( IMVFBALPHA, ~0, newVfb ) == IMVFBNULL )
|
||
|
{
|
||
|
ImVfbFree( newVfb );
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Copy the original image to the new VFB, changing
|
||
|
* depth if necessary.
|
||
|
*/
|
||
|
if ( newFields & IMVFBMONO )
|
||
|
statusVfb = ImVfbToMono( origVfb, monoThreshold,newVfb);
|
||
|
else if ( newFields & IMVFBINDEX8 )
|
||
|
statusVfb = ImVfbToIndex8( origVfb, newVfb );
|
||
|
else if ( newFields & IMVFBINDEX16 )
|
||
|
statusVfb = ImVfbToIndex16( origVfb, newVfb );
|
||
|
else if ( newFields & IMVFBRGB )
|
||
|
statusVfb = ImVfbToRgb( origVfb, newVfb );
|
||
|
else
|
||
|
{
|
||
|
/* Unknown VFB selection? How is that possible?*/
|
||
|
ImErrNo = IMENOTPOSSIBLE;
|
||
|
error = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
if ( statusVfb == IMVFBNULL )
|
||
|
{
|
||
|
error = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add a CLT if necessary.
|
||
|
*/
|
||
|
newClt = ImVfbQClt( newVfb );
|
||
|
if ( (newClt == IMCLTNULL) && (pMap->map_inAttributes & IMCLTYES) )
|
||
|
{
|
||
|
/*
|
||
|
* Note that we always generate a grayscale CLT:
|
||
|
* if Mono, no CLT means grayscale.
|
||
|
* if Index8, no CLT must default to grayscale.
|
||
|
* if Index16, no CLT must default to grayscale.
|
||
|
* if RGB, any CLT is meaningless. So grayscale.
|
||
|
* Mono gets a 2 entry CLT. All others 255 entries.
|
||
|
*/
|
||
|
if ( newFields & IMVFBMONO )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 1, 0, 255,
|
||
|
IMCLTNEW );
|
||
|
else if ( newFields & IMVFBINDEX16 )
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 65535, 0,
|
||
|
65535, IMCLTNEW);
|
||
|
else
|
||
|
newClt = ImCltGrayRamp( IMCLTNULL, 0, 255,0,
|
||
|
255, IMCLTNEW);
|
||
|
if ( newClt == IMCLTNULL )
|
||
|
{
|
||
|
error = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
ImVfbSClt( newVfb, newClt );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Add the new CLT & VFB to the tag table and continue.
|
||
|
*/
|
||
|
if ( newClt != IMCLTNULL )
|
||
|
TagTableAppend( newTable,
|
||
|
TagEntryAlloc( "image clt", POINTER, &newClt ));
|
||
|
TagTableAppend( newTable,
|
||
|
TagEntryAlloc( "image vfb", POINTER, &newVfb ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Write out the new tag table.
|
||
|
*/
|
||
|
if ( !error )
|
||
|
status = imFileWriteIt( pMap, ioType, fd, fp, pFormat,
|
||
|
flagsTable, newTable );
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Scan the new table for all the CLT's and VFB's we created
|
||
|
* and blow them away.
|
||
|
*/
|
||
|
nEntry = TagTableQNEntry( newTable, "image clt" );
|
||
|
for ( i = 0; i < nEntry; i++ )
|
||
|
{
|
||
|
tmpEntry = TagTableQDirect( newTable, "image clt", i );
|
||
|
TagEntryQValue( tmpEntry, &newClt );
|
||
|
ImCltFree( newClt );
|
||
|
}
|
||
|
nEntry = TagTableQNEntry( newTable, "image vfb" );
|
||
|
for ( i = 0; i < nEntry; i++ )
|
||
|
{
|
||
|
tmpEntry = TagTableQDirect( newTable, "image vfb", i );
|
||
|
TagEntryQValue( tmpEntry, &newVfb );
|
||
|
ImVfbFree( newVfb );
|
||
|
}
|
||
|
TagTableFree( newTable );
|
||
|
|
||
|
if ( error )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
|
||
|
static int /* Returns number of entries used*/
|
||
|
#ifdef __STDC__
|
||
|
imFileWriteIt( ImFileFormatWriteMap *pMap, int ioType, int fd, FILE *fp,
|
||
|
ImFileFormat **pFormat, TagTable *flagsTable, TagTable *tagTable )
|
||
|
#else
|
||
|
imFileWriteIt( pMap, ioType, fd, fp, pFormat, flagsTable, tagTable )
|
||
|
ImFileFormatWriteMap *pMap; /* Write map entry to use */
|
||
|
int ioType; /* I/O flags */
|
||
|
int fd; /* Output file descriptor */
|
||
|
FILE *fp; /* Output file pointer */
|
||
|
ImFileFormat **pFormat; /* Format to use */
|
||
|
TagTable *flagsTable; /* Flags */
|
||
|
TagTable *tagTable; /* Tag table to read from */
|
||
|
#endif
|
||
|
{
|
||
|
FILE *tmpFp; /* Temporary fp */
|
||
|
int tmpFd; /* Temporary fd */
|
||
|
char tmpName[1024]; /* Temporary file name */
|
||
|
unsigned char buffer[1024]; /* Temporary write buffer */
|
||
|
int status; /* Return status */
|
||
|
int n; /* Number of bytes read */
|
||
|
char message[1024]; /* Holds a message */
|
||
|
ImCompressScheme* pCompress; /* compression scheme */
|
||
|
char *tmp; /* temp string */
|
||
|
|
||
|
if ((tmp=imFileQCompression(ioType, fd, fp, flagsTable ))!=NULL)
|
||
|
{
|
||
|
pCompress = ImFileFindCompressionScheme(tmp);
|
||
|
sprintf(message,"'%s' (%s)",pCompress->compress_suffixes[0],pCompress->compress_name);
|
||
|
ImInfo("File Compression",message);
|
||
|
sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help);
|
||
|
ImInfo("Image Format",message);
|
||
|
status = imFileWriteCompressedFile(ioType, fd, fp, flagsTable, tagTable, pMap, tmp);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Write an uncompressed file */
|
||
|
sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help);
|
||
|
ImInfo("Image Format",message);
|
||
|
|
||
|
/*
|
||
|
* If the fd/fp is a file, or it's a pipe and the handler can write
|
||
|
* directly to a pipe, write it.
|
||
|
*/
|
||
|
if ( ioType & IMFILEIOFILE || (*pFormat)->format_writePipe == IMPIPE )
|
||
|
return ( (*pMap->map_write)( pMap, ioType, fd, fp,
|
||
|
flagsTable, tagTable ) );
|
||
|
|
||
|
/*
|
||
|
* Create a temporary file. Write to it, then copy it out to the
|
||
|
* stream and delete it. If errors occur, save them until the
|
||
|
* unlink has been done.
|
||
|
*/
|
||
|
strcpy( tmpName, imMakeTempFile() );
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
status = (*pMap->map_write)( pMap, IMFILEIOFD | IMFILEIOFILE,
|
||
|
tmpFd, NULL, flagsTable, tagTable );
|
||
|
lseek( tmpFd, 0, 0 );
|
||
|
while ( (n = read( tmpFd, buffer, sizeof( buffer ) )) > 0 )
|
||
|
write( fd, buffer, n );
|
||
|
close( tmpFd );
|
||
|
unlink( tmpName );
|
||
|
return ( status );
|
||
|
}
|
||
|
if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
status = (*pMap->map_write)( pMap, IMFILEIOFP | IMFILEIOFILE, -1, tmpFp,
|
||
|
flagsTable, tagTable );
|
||
|
fseek( tmpFp, 0, 0 );
|
||
|
while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 )
|
||
|
fwrite( buffer, 1, n, fp );
|
||
|
fclose( tmpFp );
|
||
|
unlink( tmpName );
|
||
|
}
|
||
|
return ( status );
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTIONS
|
||
|
* ImFileQCompression
|
||
|
* ImFileQFCompression
|
||
|
* imFileQCompression
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Query the compression scheme of a file using the
|
||
|
* file descriptor, file pointer, or ioType.
|
||
|
*
|
||
|
*/
|
||
|
char *
|
||
|
#ifdef __STDC__
|
||
|
ImFileQCompression( int fd, TagTable* flagsTable)
|
||
|
#else
|
||
|
ImFileQCompression( fd, flagsTable)
|
||
|
int fd;
|
||
|
TagTable* flagsTable;
|
||
|
#endif
|
||
|
{
|
||
|
return imFileQCompression(IOTYPE(fd), fd, NULL, flagsTable);
|
||
|
}
|
||
|
|
||
|
char *
|
||
|
#ifdef __STDC__
|
||
|
ImFileQFCompression( FILE* fp, TagTable* flagsTable)
|
||
|
#else
|
||
|
ImFileQFCompression( fp, flagsTable)
|
||
|
FILE* fp;
|
||
|
TagTable* flagsTable;
|
||
|
#endif
|
||
|
{
|
||
|
return imFileQCompression(FIOTYPE(fp), -1, fp, flagsTable);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imFileQCompression
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Determine whether or not a file is compressed. If it is,
|
||
|
* return the name of the compression scheme.
|
||
|
*/
|
||
|
static char * /* returns name of compression scheme or NULL */
|
||
|
#ifdef __STDC__
|
||
|
imFileQCompression(int ioType, int fd, FILE* fp, TagTable* flagsTable)
|
||
|
#else
|
||
|
imFileQCompression(ioType, fd, fp, flagsTable)
|
||
|
int ioType;
|
||
|
int fd;
|
||
|
FILE* fp;
|
||
|
TagTable* flagsTable;
|
||
|
#endif
|
||
|
{
|
||
|
char* fileName; /* file name */
|
||
|
TagEntry* tmpEntry; /* holds a tag table entry */
|
||
|
int doMagic; /* should we check magic#? */
|
||
|
int ioOperation; /* Read or write? */
|
||
|
char *strTmp; /* holds a string */
|
||
|
int i; /* index */
|
||
|
int length; /* length of a magic # */
|
||
|
ImCompressScheme**ppScheme;/* points into the table of schemes */
|
||
|
ImCompressScheme*pScheme; /* points into the table of schemes */
|
||
|
unsigned char magic1[IMMAXMAGIC]; /* Test magic number */
|
||
|
unsigned char magic2[IMMAXMAGIC]; /* another magic # */
|
||
|
unsigned char *magic; /* current magic # */
|
||
|
unsigned char *fmtMagic; /* current format's magic # */
|
||
|
ImFileMagic *pMagic; /* pointer to magic file info */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* We are compressed if...
|
||
|
* the file compression option is in the flags table
|
||
|
* OR we have one of the magic numbers in the compression table
|
||
|
* OR the filename in the flags table ends in a suffix in the compression table
|
||
|
*/
|
||
|
|
||
|
if (TagTableQNEntry( flagsTable, "file compression" )>0)
|
||
|
{
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file compression", 0 );
|
||
|
TagEntryQValue( tmpEntry, &strTmp);
|
||
|
if (ImFileFindCompressionScheme(strTmp)!=NULL)
|
||
|
return strTmp;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Should we check the magic number?
|
||
|
* 1. If file is a pipe or a tty, NO.
|
||
|
* 2. If file is open for writing only, NO.
|
||
|
* 3. Otherwise, YES.
|
||
|
*/
|
||
|
doMagic = TRUE;
|
||
|
#if 0
|
||
|
if ( ioType & IMFILEIOPIPE )
|
||
|
doMagic = FALSE; /* Input is a pipe or a tty */
|
||
|
else
|
||
|
{
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
ioOperation = fcntl( fd, F_GETFL, &ioOperation );
|
||
|
else
|
||
|
ioOperation = fcntl( fileno(fp), F_GETFL, &ioOperation);
|
||
|
if ( ioOperation == -1 )
|
||
|
{
|
||
|
ImErrNo = IMESYS; /* Bad fp/fd? */
|
||
|
return ( 0 );
|
||
|
}
|
||
|
if ( ioOperation == O_WRONLY )
|
||
|
doMagic = FALSE;
|
||
|
}
|
||
|
#endif
|
||
|
/*
|
||
|
* Get the magic number from the file and check it against each of
|
||
|
* the ones in the compression scheme list.
|
||
|
*/
|
||
|
if (doMagic)
|
||
|
{
|
||
|
/*
|
||
|
* Read in the magic number at location 0 (where most are)
|
||
|
*/
|
||
|
if (ioType & IMFILEIOFD)
|
||
|
{
|
||
|
IMREADMAGIC( fd, magic1, IMMAXMAGIC, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IMREADFMAGIC( fp, magic1, IMMAXMAGIC, 0 );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check it against the scheme list. If a scheme
|
||
|
* doesn't have a magic number, skip it. If it has
|
||
|
* a magic number, but not at byte 0, then read in
|
||
|
* and test with that magic. Otherwise test with the
|
||
|
* the magic number ead in above.
|
||
|
*/
|
||
|
|
||
|
magic = magic1;
|
||
|
for (ppScheme = ImGetCompressSchemes(); *ppScheme; ppScheme++)
|
||
|
{
|
||
|
pMagic = (*ppScheme)->compress_magic_numbers;
|
||
|
if (pMagic == NULL )
|
||
|
{
|
||
|
/* No magic numbers */
|
||
|
continue;
|
||
|
}
|
||
|
for ( ; pMagic->format_magicNumber != NULL; pMagic++)
|
||
|
{
|
||
|
if (pMagic->format_magicLength==0)
|
||
|
continue; /* no magic number */
|
||
|
if (pMagic->format_magicLocation != 0 )
|
||
|
{
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
IMREADMAGIC( fd, magic2,
|
||
|
pMagic->format_magicLength,
|
||
|
pMagic->format_magicLocation );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IMREADFMAGIC( fp, magic2,
|
||
|
pMagic->format_magicLength,
|
||
|
pMagic->format_magicLocation );
|
||
|
}
|
||
|
magic = magic2;
|
||
|
}
|
||
|
length = pMagic->format_magicLength;
|
||
|
fmtMagic = pMagic->format_magicNumber;
|
||
|
|
||
|
/* See if it matches the magic number */
|
||
|
for (i=0; i < length; i++)
|
||
|
if (fmtMagic[i] != magic[i] )
|
||
|
break;
|
||
|
if (i==length)
|
||
|
return ( (*ppScheme)->compress_suffixes[0] );
|
||
|
magic = magic1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* No magic number match. Check the file name.
|
||
|
*/
|
||
|
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
{
|
||
|
TagEntryQValue( tmpEntry, &fileName);
|
||
|
/*
|
||
|
* Set strTmp to be the one more than the last period
|
||
|
* in the filename.
|
||
|
*/
|
||
|
strTmp = fileName + strlen(fileName) - 1;
|
||
|
while (*strTmp!='.' && strTmp!=fileName)
|
||
|
strTmp--;
|
||
|
if (*strTmp=='.')
|
||
|
{
|
||
|
strTmp++;
|
||
|
if ( (pScheme=ImFileFindCompressionScheme(strTmp))!=NULL)
|
||
|
return pScheme->compress_suffixes[0];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imFileWriteCompressedFile
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Write a compressed file from the tagTable
|
||
|
*/
|
||
|
static int /* returns status */
|
||
|
#ifdef __STDC__
|
||
|
imFileWriteCompressedFile(int ioType, int fd, FILE* fp, TagTable* flagsTable, TagTable* tagTable,
|
||
|
ImFileFormatWriteMap *pMap, char* scheme)
|
||
|
#else
|
||
|
imFileWriteCompressedFile(ioType, fd, fp, flagsTable, tagTable, pMap, scheme)
|
||
|
int ioType;
|
||
|
int fd;
|
||
|
FILE* fp;
|
||
|
TagTable* flagsTable;
|
||
|
TagTable* tagTable;
|
||
|
ImFileFormatWriteMap *pMap;
|
||
|
char* scheme; /* Compression scheme to use */
|
||
|
#endif
|
||
|
{
|
||
|
char tmpName[1024]; /* uncompressed file */
|
||
|
char newName[1024]; /* compressed file */
|
||
|
FILE* tmpFp; /* a file pointer */
|
||
|
int status; /* status */
|
||
|
int n; /* bytes read */
|
||
|
unsigned char buffer[1024]; /* Temporary write buffer */
|
||
|
ImCompressScheme* pScheme; /* points into our table */
|
||
|
|
||
|
/*
|
||
|
* 1. Dump all of the data into a temp file.
|
||
|
* 2. Compress the temp file using the write routine from our table.
|
||
|
* (This will destroy the uncompressed file)
|
||
|
* 3. Dump the compressed file to our output stream.
|
||
|
* 4. Remove the compressed file.
|
||
|
*/
|
||
|
strcpy(tmpName,imMakeTempFile());
|
||
|
tmpFp = fopen(tmpName,"wb");
|
||
|
if (tmpFp==NULL)
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
status = (*pMap->map_write)( pMap, IMFILEIOFP | IMFILEIOFILE, -1, tmpFp,
|
||
|
flagsTable, tagTable );
|
||
|
fclose(tmpFp);
|
||
|
|
||
|
/*
|
||
|
* Look up the compression scheme
|
||
|
*/
|
||
|
pScheme = ImFileFindCompressionScheme(scheme);
|
||
|
if (pScheme==NULL)
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ( pScheme->compress_encode == NULL )
|
||
|
{
|
||
|
ImErrNo = IMENOREAD;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compress our temp file, and destroy the uncompressed one.
|
||
|
*/
|
||
|
if ((*pScheme->compress_encode)(tmpName, newName, flagsTable)==-1)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
tmpFp = fopen(newName,"rb");
|
||
|
if (tmpFp==NULL)
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Send the new file to the outgoing stream
|
||
|
*/
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 )
|
||
|
write( fd, buffer, n );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
while ( (n = fread( buffer, 1, sizeof( buffer ), tmpFp )) != 0 )
|
||
|
fwrite( buffer, 1, n, fp );
|
||
|
}
|
||
|
fclose(tmpFp);
|
||
|
unlink(newName);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imFileReadCompressedFile
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Read a compressed file into the tagTable
|
||
|
*/
|
||
|
static int
|
||
|
#ifdef __STDC__
|
||
|
imFileReadCompressedFile(int ioType, int fd, FILE* fp, TagTable* flagsTable, TagTable* tagTable, char* scheme)
|
||
|
#else
|
||
|
imFileReadCompressedFile(ioType, fd, fp, flagsTable, tagTable, scheme)
|
||
|
int ioType;
|
||
|
int fd;
|
||
|
FILE* fp;
|
||
|
TagTable* flagsTable;
|
||
|
TagTable* tagTable;
|
||
|
char* scheme;
|
||
|
#endif
|
||
|
{
|
||
|
char tmpName[1024]; /* compressed file */
|
||
|
char newName[1024]; /* uncompressed file */
|
||
|
FILE* tmpFp; /* holds an fp */
|
||
|
int tmpFd; /* holds an fd */
|
||
|
unsigned char buffer[1024]; /* Temporary write buffer */
|
||
|
int status = 1; /* status */
|
||
|
int n; /* num of bytes read */
|
||
|
ImFileFormat **pFormat; /* format that we're reading */
|
||
|
char* fileName; /* Name of file (in flagsTable) */
|
||
|
TagEntry* tmpEntry; /* holds a tag table entry */
|
||
|
char* format; /* format name */
|
||
|
char message[1024]; /* holds a message */
|
||
|
ImCompressScheme* pScheme; /* compression scheme entry */
|
||
|
|
||
|
/*
|
||
|
* We could do things here with lots of processes
|
||
|
* and lots of pipes. However, that would get
|
||
|
* quite messy. Instead we're just going to
|
||
|
* use temp files.
|
||
|
*
|
||
|
* Algorithm:
|
||
|
*
|
||
|
* 1. Put all the data in a temp file
|
||
|
* 2. Uncompress the temp file
|
||
|
* 3. Read the uncompressed file
|
||
|
* 4. Destroy both files.
|
||
|
*/
|
||
|
strcpy( tmpName , imMakeTempFile() );
|
||
|
|
||
|
/*
|
||
|
* Write all the data
|
||
|
*/
|
||
|
if ( ioType & IMFILEIOFD )
|
||
|
{
|
||
|
if ( (tmpFd = open( tmpName, O_RDWR|O_CREAT, 0666 )) == -1 )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
while ( (n = read( fd, buffer, sizeof( buffer ) )) > 0 )
|
||
|
write( tmpFd, buffer, n );
|
||
|
close( tmpFd );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( (tmpFp = fopen( tmpName, "w+b" )) == NULL )
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
while ( (n = fread( buffer, 1, sizeof( buffer ), fp )) != 0 )
|
||
|
fwrite( buffer, 1, n, tmpFp );
|
||
|
/* Write an EOF */
|
||
|
fclose( tmpFp );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Call the scheme's read routine.
|
||
|
* This routine will uncompress the file,
|
||
|
* delete the file we pass it,
|
||
|
* and tell us the name of the new file.
|
||
|
*/
|
||
|
pScheme = ImFileFindCompressionScheme(scheme);
|
||
|
if (pScheme==NULL)
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return NULL;
|
||
|
}
|
||
|
if ( pScheme->compress_decode == NULL )
|
||
|
{
|
||
|
ImErrNo = IMENOREAD;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
if ((*pScheme->compress_decode)(tmpName, newName, flagsTable)==-1)
|
||
|
{
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read the uncompressed file
|
||
|
*/
|
||
|
tmpFp = fopen(newName,"rb");
|
||
|
if (tmpFp==NULL)
|
||
|
{
|
||
|
ImErrNo = IMESYS;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* What format is the image file?
|
||
|
*/
|
||
|
tmpEntry = TagTableQDirect( flagsTable, "file name", 0 );
|
||
|
if ( tmpEntry != TAGENTRYNULL )
|
||
|
{
|
||
|
TagEntryQValue( tmpEntry, &fileName);
|
||
|
}
|
||
|
else
|
||
|
fileName = NULL;
|
||
|
if ((format = ImFileQFFormat(tmpFp, fileName))==NULL)
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Look up the format
|
||
|
*/
|
||
|
if ( (pFormat = ImFileFindFormat( format )) == NULL )
|
||
|
{
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
if ( (*pFormat)->format_read == NULL )
|
||
|
{
|
||
|
ImErrNo = IMENOREAD;
|
||
|
ImErrorFatal( ImQError( ), -1, ImErrNo );
|
||
|
}
|
||
|
|
||
|
sprintf(message,"'%s' (%s)",(*pFormat)->format_names[0],(*pFormat)->format_help);
|
||
|
ImInfo("Image Format",message);
|
||
|
|
||
|
|
||
|
status = (*(*pFormat)->format_read)( IMFILEIOFP | IMFILEIOFILE, -1,
|
||
|
tmpFp, flagsTable, tagTable );
|
||
|
/*
|
||
|
* Destroy the uncompressed file
|
||
|
*/
|
||
|
unlink(newName);
|
||
|
|
||
|
return status;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* ImFileFindCompressionScheme
|
||
|
*
|
||
|
* DESCRIPTION
|
||
|
* Search through the compression table for this
|
||
|
* scheme.
|
||
|
*/
|
||
|
ImCompressScheme *
|
||
|
#ifdef __STDC__
|
||
|
ImFileFindCompressionScheme( char* scheme)
|
||
|
#else
|
||
|
ImFileFindCompressionScheme(scheme)
|
||
|
char* scheme;
|
||
|
#endif
|
||
|
{
|
||
|
int found; /* Have we found it yet? */
|
||
|
ImCompressScheme** pCompress; /* points into the table */
|
||
|
char** strPtr; /* points to list of strs */
|
||
|
|
||
|
found = 0;
|
||
|
pCompress = ImGetCompressSchemes();
|
||
|
while (!found && *pCompress)
|
||
|
{
|
||
|
/*
|
||
|
* Check all the names for this scheme
|
||
|
*/
|
||
|
strPtr = (*pCompress)->compress_suffixes;
|
||
|
while (*strPtr!=NULL && strcmp(*strPtr,scheme)!=0)
|
||
|
strPtr++;
|
||
|
if (*strPtr!=NULL)
|
||
|
found = 1;
|
||
|
if (!found)
|
||
|
pCompress++;
|
||
|
}
|
||
|
if (!found)
|
||
|
{
|
||
|
ImErrNo = IMEFORMATUNKNOWN;
|
||
|
return NULL;
|
||
|
}
|
||
|
return *pCompress;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FUNCTION
|
||
|
* imMakeTempFile
|
||
|
* DESCRIPTION
|
||
|
* make file name for a temp file.
|
||
|
* This name will have the full path of
|
||
|
* the temp file. (i.e. in the correct
|
||
|
* temp directory)
|
||
|
*/
|
||
|
static char*
|
||
|
#ifdef __STDC__
|
||
|
imMakeTempFile(void)
|
||
|
#else
|
||
|
imMakeTempFile()
|
||
|
#endif
|
||
|
{
|
||
|
char* tmpDir; /* temp directory */
|
||
|
static char ret[1024]; /* string to return */
|
||
|
|
||
|
/* Get tmpDir from environment. */
|
||
|
tmpDir = getenv("TMPDIR");
|
||
|
if (tmpDir==NULL)
|
||
|
sprintf(ret,"/tmp/im.XXXXXX");
|
||
|
else
|
||
|
sprintf(ret,"%s/im.XXXXXX",tmpDir);
|
||
|
mktemp(ret);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|