blob: 2ed70d6e12d85351621b00c85ab26149ab27f1ed [file] [log] [blame]
/* @(#) im_open: vips front end to file open functions
ARGS: filename and mode which is one of:
"r" read by mmap (im_mmapin)
"rw" read and write by mmap (im_mmapinrw)
"w" write for write (im_writeline)
"t" for temporary memory image
"p" for partial memory image
RETURNS: IMAGE pointer or 0 on error
Copyright: Kirk Martinez 29/5/92
Modified:
* 8/8/94 JC
* - ANSIfied
* - im_open_local added
* 16/8/94 JC
* - im_malloc() added
* 22/11/94 JC & KM
* - tiff added
* 1/2/95 JC
* - tiff removed again!
* - now just im_istiff(), im_tiff2vips() and im_vips2tiff()
* - applications have responsibility for making the translation
* 26/3/96 JC
* - im_open_local() now closes on close, not evalend
* 14/11/96 JC
* - tiff and jpeg added again
* - open for read used im_istiff() and im_isjpeg() to switch
* - open for write looks at suffix
* 23/4/97 JC
* - im_strdup() now allows NULL IMAGE parameter
* - subsample parameter added to im_tiff2vips()
* 29/10/98 JC
* - byte-swap stuff added
* 16/6/99 JC
* - 8x byte swap added for double/double complex
* - better error message if file does not exist
* - ignore case when testing suffix for save
* - fix im_mmapinrw() return test to stop coredump in edvips if
* unwritable
* 2/11/99 JC
* - malloc/strdup stuff moved to memory.c
* 5/8/00 JC
* - fixes for im_vips2tiff() changes
* 13/10/00 JC
* - ooops, missing 'else'
* 22/11/00 JC
* - ppm read/write added
* 12/2/01 JC
* - im__image_sanity() added
* 16/5/01 JC
* - now allows RW for non-native byte order, provided it's an 8-bit
* image
* 11/7/01 JC
* - im_tiff2vips() no longer has subsample option
* 25/3/02 JC
* - better im_open() error message
* 12/12/02 JC
* - sanity no longer returns errors, just warns
* 28/12/02 HB
* - Added PNG support
* 6/5/03 JC
* - added im_open_header() (from header.c)
* 22/5/03 JC
* - im__skip_dir() knows about ':' in filenames for vips2tiff etc.
* 27/11/03 JC
* - im_image_sanity() now insists x/y/bands all >0
* 6/1/04 JC
* - moved util funcs out to util.c
* 18/2/04 JC
* - added an im_init_world() to help old progs
* 16/4/04
* - oop, im_open() didn't know about input options in filenames
* 2/11/04
* - im_open( "r" ) is now lazier ... for non-VIPS formats, we delay
* read until the first ->generate()
* - so im_open_header() is now a no-op
* 22/6/05
* - if TIFF open fails, try going through libmagick
* 4/8/05
* - added analyze read
* 30/9/05
* - oops, lazy read error recovery didn't clear a pointer
* 1/5/06
* - added OpenEXR read
* 9/6/06
* - added CSV read/write
* 20/9/06
* - test for NULL filename/mode, common if you forget to check argc
* (thanks bruno)
* 7/11/07
* - use preclose, not evalend, for delayed save
* - add simple cmd-line progress feedback
* 9/8/08
* - lock global image list (thanks lee)
* 25/5/08
* - break file format stuff out to the new pluggable image format system
* 14/1/09
* - write to non-vips formats with a "written" callback
*/
/*
This file is part of VIPS.
VIPS 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 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <vips/vips.h>
#include <vips/debug.h>
#include <vips/internal.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#endif /*WITH_DMALLOC*/
/* Progress feedback. Only really useful for testing, tbh.
*/
int im__progress = 0;
/* Delayed save: if we write to (eg.) TIFF, actually do the write
* to a "p" and on "written" do im_vips2tiff() or whatever. Track save
* parameters here.
*/
typedef struct {
int (*save_fn)(); /* Save function */
IMAGE *im; /* Image to save */
char *filename; /* Save args */
} SaveBlock;
/* From "written" callback: invoke a delayed save.
*/
static int
invoke_sb( SaveBlock *sb )
{
if( sb->save_fn( sb->im, sb->filename ) )
return( -1 );
return( 0 );
}
/* Attach a SaveBlock to an image.
*/
static int
attach_sb( IMAGE *out, int (*save_fn)(), const char *filename )
{
SaveBlock *sb = IM_NEW( out, SaveBlock );
if( !sb )
return( -1 );
sb->im = out;
sb->save_fn = save_fn;
sb->filename = im_strdup( out, filename );
if( im_add_written_callback( out,
(im_callback_fn) invoke_sb, (void *) sb, NULL ) )
return( -1 );
return( 0 );
}
/* Lazy open: init a descriptor from a filename (just read the header) but
* delay actually decoding pixels until the first call to a start function.
*/
typedef int (*OpenLazyFn)( const char *filename, IMAGE *im );
/* What we track during a delayed open.
*/
typedef struct _OpenLazy {
char *filename;
OpenLazyFn read_pixels; /* Read in pixels with this */
IMAGE *lazy_im; /* Image we read to .. copy from this */
} OpenLazy;
/* Our start function ... do the lazy open, if necessary, and return a region
* on the new image.
*/
static void *
open_lazy_start( IMAGE *out, void *a, void *dummy )
{
OpenLazy *lazy = (OpenLazy *) a;
if( !lazy->lazy_im ) {
if( !(lazy->lazy_im = im_open_local( out, "read", "p" )) ||
lazy->read_pixels( lazy->filename, lazy->lazy_im ) ) {
IM_FREEF( im_close, lazy->lazy_im );
return( NULL );
}
}
return( im_region_create( lazy->lazy_im ) );
}
/* Just copy.
*/
static int
open_lazy_generate( REGION *or, void *seq, void *a, void *b )
{
REGION *ir = (REGION *) seq;
Rect *r = &or->valid;
/* Ask for input we need.
*/
if( im_prepare( ir, r ) )
return( -1 );
/* Attach output region to that.
*/
if( im_region_region( or, ir, r, r->left, r->top ) )
return( -1 );
return( 0 );
}
/* Lazy open ... init the header with the first OpenLazyFn, delay actually
* decoding pixels with the second OpenLazyFn until the first generate().
*/
static int
open_lazy( OpenLazyFn read_header, OpenLazyFn read_pixels,
const char *filename, IMAGE *out )
{
OpenLazy *lazy = IM_NEW( out, OpenLazy );
if( !lazy ||
!(lazy->filename = im_strdup( out, filename )) )
return( -1 );
lazy->read_pixels = read_pixels;
lazy->lazy_im = NULL;
if( read_header( filename, out ) ||
im_demand_hint( out, IM_ANY, NULL ) )
return( -1 );
if( im_generate( out,
open_lazy_start, open_lazy_generate, im_stop_one,
lazy, NULL ) )
return( -1 );
return( 0 );
}
static IMAGE *
open_sub( OpenLazyFn read_header, OpenLazyFn read_pixels, const char *filename )
{
IMAGE *im;
if( !(im = im_open( filename, "p" )) ||
open_lazy( read_header, read_pixels, filename, im ) ) {
im_close( im );
return( NULL );
}
return( im );
}
/* Progress feedback.
*/
/* What we track during an eval.
*/
typedef struct {
IMAGE *im;
int last_percent; /* The last %complete we displayed */
} Progress;
int
evalstart_cb( Progress *progress )
{
progress->last_percent = 0;
return( 0 );
}
int
eval_cb( Progress *progress )
{
IMAGE *im = progress->im;
if( im->time->percent != progress->last_percent ) {
printf( _( "%s %s: %d%% complete" ),
g_get_prgname(), im->filename, im->time->percent );
printf( "\r" );
fflush( stdout );
progress->last_percent = im->time->percent;
}
return( 0 );
}
int
evalend_cb( Progress *progress )
{
IMAGE *im = progress->im;
/* Spaces at end help to erase the %complete message we overwrite.
*/
printf( _( "%s %s: done in %ds \n" ),
g_get_prgname(), im->filename, im->time->run );
return( 0 );
}
/**
* im_open:
* @filename: file to open
* @mode: mode to open with
*
* im_open() examines the mode string, and creates an appropriate #IMAGE.
*
* <itemizedlist>
* <listitem>
* <para>
* <emphasis>"r"</emphasis>
* opens the named file for reading. If the file is not in the native
* VIPS format for your machine, im_open() automatically converts the
* file for you in memory.
*
* For some large files (eg. TIFF) this may
* not be what you want: you should call the appropriate converter
* yourself, and arrange for the conversion to take place on disc.
* See #VipsFormat.
*
* im_open() can read files in most formats.
*
* Note that <emphasis>"r"</emphasis> mode works in at least two stages.
* It should return quickly and let you check header fields. It will
* only actually read in pixels when you first access them.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"w"</emphasis>
* opens the named file for writing. It looks at the file name
* suffix to determine the type to write -- for example:
*
* |[
* im_open( "fred.tif", "w" )
* ]|
*
* will write in TIFF format.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"t"</emphasis>
* creates a temporary memory buffer image.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"p"</emphasis>
* creates a "glue" descriptor you can use to join two image
* processing operations together.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"rw"</emphasis>
* opens the named file for reading and writing. This will only work for
* VIPS files in a format native to your machine. It is only for
* paintbox-type applications.
* </para>
* </listitem>
* </itemizedlist>
*
* See also: im_close(), #VipsFormat
*
* Returns: the image descriptor on success and NULL on error.
*/
IMAGE *
im_open( const char *filename, const char *mode )
{
IMAGE *im;
VipsFormatClass *format;
/* Pass in a nonsense name for argv0 ... this init world is only here
* for old programs which are missing an im_init_world() call. We must
* have threads set up before we can process.
*/
if( im_init_world( "vips" ) )
im_error_clear();
if( !filename || !mode ) {
im_error( "im_open", "%s", _( "NULL filename or mode" ) );
return( NULL );
}
/*
we can't use the vips handler in the format system, since we
want to be able to open the image directly rather than
copying to an existing descriptor
if we don't do this, things like paintbox apps won't work
*/
switch( mode[0] ) {
case 'r':
if( (format = vips_format_for_file( filename )) ) {
if( strcmp( VIPS_OBJECT_CLASS( format )->nickname,
"vips" ) == 0 ) {
if( !(im = im_open_vips( filename )) )
return( NULL );
}
else if( !(im = open_sub(
format->header, format->load, filename )) )
return( NULL );
}
else
return( NULL );
break;
case 'w':
if( (format = vips_format_for_name( filename )) ) {
if( strcmp( VIPS_OBJECT_CLASS( format )->nickname,
"vips" ) == 0 )
im = im_openout( filename );
else {
if( !(im = im_open( filename, "p" )) )
return( NULL );
if( attach_sb( im, format->save, filename ) ) {
im_close( im );
return( NULL );
}
}
}
else {
char suffix[FILENAME_MAX];
im_filename_suffix( filename, suffix );
im_error( "im_open",
_( "unsupported filetype \"%s\"" ),
suffix );
return( NULL );
}
break;
case 't':
im = im_setbuf( filename );
break;
case 'p':
im = im_partial( filename );
break;
default:
im_error( "im_open", _( "bad mode \"%s\"" ), mode );
return( NULL );
}
/* Attach progress feedback, if required.
*/
if( im__progress || g_getenv( "IM_PROGRESS" ) ) {
Progress *progress = IM_NEW( im, Progress );
progress->im = im;
im_add_evalstart_callback( im,
(im_callback_fn) evalstart_cb, progress, NULL );
im_add_eval_callback( im,
(im_callback_fn) eval_cb, progress, NULL );
im_add_evalend_callback( im,
(im_callback_fn) evalend_cb, progress, NULL );
}
#ifdef DEBUG
printf( "im_open: success for %s (%p)\n", im->filename, im );
#endif /*DEBUG*/
return( im );
}
/* Just here for compatibility.
*/
IMAGE *
im_open_header( const char *file )
{
return( im_open( file, "r" ) );
}