diff --git a/TODO b/TODO index 52975e92..f68328ed 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,8 @@ +- read_vips() in im_open.c:179 needs moving to format and breaking into is_a, + header, load and save, as tiff etc. + + make im_file2vips, im_vips2file? + - pluggable formats for each format, store: diff --git a/include/vips/dispatch.h b/include/vips/dispatch.h index 18cc4170..48bfca51 100644 --- a/include/vips/dispatch.h +++ b/include/vips/dispatch.h @@ -300,6 +300,12 @@ im_function *im_find_function( const char *name ); im_package *im_find_package( const char *name ); im_package *im_package_of_function( const char *name ); +/* Map over and find formats. + */ +void *im_map_formats( VSListMap2Fn fn, void *a, void *b ); +im_format *im_format_for_file( const char *filename ); +im_format *im_format_for_name( const char *filename ); + /* Allocate space for, and free im_object argument lists. */ int im_free_vargv( im_function *fn, im_object *vargv ); diff --git a/libsrc/format/format_dispatch.c b/libsrc/format/format_dispatch.c index 36938d8c..a48ee804 100644 --- a/libsrc/format/format_dispatch.c +++ b/libsrc/format/format_dispatch.c @@ -418,5 +418,124 @@ im_package im__format = { list }; +/* Suffix sets. + */ +static const char *tiff_suffs[] = { ".tif", ".tiff", NULL }; +static const char *jpeg_suffs[] = { ".jpg", ".jpeg", ".jpe", NULL }; +static const char *png_suffs[] = { ".png", NULL }; +static const char *csv_suffs[] = { ".csv", NULL }; +static const char *ppm_suffs[] = { ".ppm", ".pgm", ".pbm", NULL }; +static const char *exr_suffs[] = { ".exr", NULL }; +static const char *analyze_suffs[] = { ".img", ".hdr", NULL }; +static const char *magick_suffs[] = { NULL }; +/* VIPS image formats. + */ +static im_format jpeg_desc = { + "jpeg", /* internal name */ + N_( "JPEG" ), /* i18n'd visible name */ + 0, /* Priority */ + jpeg_suffs, /* Allowed suffixes */ + im_isjpeg, /* is_a */ + im_jpeg2vips_header, /* Load header only */ + im_jpeg2vips, /* Load */ + im_vips2jpeg /* Save */ +}; +static im_format tiff_desc = { + "tiff", /* internal name */ + N_( "TIFF" ), /* i18n'd visible name */ + 0, /* Priority */ + tiff_suffs, /* Allowed suffixes */ + im_istiff, /* is_a */ + im_tiff2vips_header, /* Load header only */ + im_tiff2vips, /* Load */ + im_vips2tiff /* Save */ +}; + +static im_format png_desc = { + "png", /* internal name */ + N_( "PNG" ), /* i18n'd visible name */ + 0, /* Priority */ + png_suffs, /* Allowed suffixes */ + im_ispng, /* is_a */ + im_png2vips_header, /* Load header only */ + im_png2vips, /* Load */ + im_vips2png /* Save */ +}; + +static im_format csv_desc = { + "csv", /* internal name */ + N_( "CSV" ), /* i18n'd visible name */ + 0, /* Priority */ + csv_suffs, /* Allowed suffixes */ + NULL, /* is_a */ + im_csv2vips_header, /* Load header only */ + im_csv2vips, /* Load */ + im_vips2csv /* Save */ +}; + +static im_format ppm_desc = { + "ppm", /* internal name */ + N_( "PPM/PBM/PNM" ), /* i18n'd visible name */ + 0, /* Priority */ + ppm_suffs, /* Allowed suffixes */ + im_isppm, /* is_a */ + im_ppm2vips_header, /* Load header only */ + im_ppm2vips, /* Load */ + im_vips2ppm /* Save */ +}; + +static im_format analyze_desc = { + "analyze", /* internal name */ + N_( "Analyze 6.0" ), /* i18n'd visible name */ + 0, /* Priority */ + analyze_suffs, /* Allowed suffixes */ + im_isanalyze, /* is_a */ + im_analyze2vips_header, /* Load header only */ + im_analyze2vips, /* Load */ + NULL /* Save */ +}; + +static im_format exr_desc = { + "exr", /* internal name */ + N_( "OpenEXR" ), /* i18n'd visible name */ + 0, /* Priority */ + exr_suffs, /* Allowed suffixes */ + im_isexr, /* is_a */ + im_exr2vips_header, /* Load header only */ + im_exr2vips, /* Load */ + NULL /* Save */ +}; + +static im_format magick_desc = { + "magick", /* internal name */ + N_( "ImageMagick-supported format" ), /* i18n'd visible name */ + -1000, /* Priority */ + magick_suffs, /* Allowed suffixes */ + im_ismagick, /* is_a */ + im_magick2vips_header, /* Load header only */ + im_magick2vips, /* Load */ + NULL /* Save */ +}; + +/* Package up all these formats. + */ +static im_format *format_list[] = { + &csv_desc, + &jpeg_desc, + &magick_desc, + &png_desc, + &exr_desc, + &ppm_desc, + &analyze_desc, + &tiff_desc +}; + +/* Package of format. + */ +im_format_package im__format_format = { + "format", + IM_NUMBER( format_list ), + format_list +}; diff --git a/libsrc/iofuncs/im_open.c b/libsrc/iofuncs/im_open.c index fb712565..e0ee4903 100644 --- a/libsrc/iofuncs/im_open.c +++ b/libsrc/iofuncs/im_open.c @@ -225,6 +225,30 @@ read_vips( const char *filename ) return( im2 ); } +/* + else if( im_isvips( name ) ) { + if( mode[1] == 'w' ) { + if( !(im = im_init( filename )) ) + return( NULL ); + if( im_openinrw( im ) ) { + im_close( im ); + return( NULL ); + } + if( im->Bbits != IM_BBITS_BYTE && + im_isMSBfirst( im ) != + im_amiMSBfirst() ) { + im_close( im ); + im_error( "im_open", _( "open for read-" + "write for native format " + "images only" ) ); + return( NULL ); + } + } + else + im = read_vips( filename ); + } + */ + /* Delayed save: if we write to TIFF or to JPEG format, actually do the write * to a "p" and on preclose do im_vips2tiff() or whatever. Track save * parameters here. @@ -416,6 +440,7 @@ IMAGE * im_open( const char *filename, const char *mode ) { IMAGE *im; + im_format *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 @@ -431,107 +456,11 @@ im_open( const char *filename, const char *mode ) switch( mode[0] ) { case 'r': -{ - char name[FILENAME_MAX]; - char options[FILENAME_MAX]; - - /* Break any options off the name ... eg. "fred.tif:jpeg,tile" - * etc. - */ - im_filename_split( filename, name, options ); - - /* Check for other formats. - - FIXME ... should have a table to avoid all this - repetition - - */ - if( !im_existsf( "%s", name ) ) { - im_error( "im_open", - _( "\"%s\" is not readable" ), name ); + if( !(format = im_format_for_file( filename )) ) return( NULL ); - } - else if( im_istiff( name ) ) { - /* If TIFF open fails, try going through libmagick. - */ - if( !(im = open_sub( - im_tiff2vips_header, im_tiff2vips, - filename )) && - !(im = open_sub( - im_magick2vips_header, im_magick2vips, - filename )) ) - return( NULL ); - } - else if( im_isjpeg( name ) ) { - if( !(im = open_sub( - im_jpeg2vips_header, im_jpeg2vips, filename )) ) - return( NULL ); - } - else if( im_isexr( name ) ) { - if( !(im = open_sub( - im_exr2vips_header, im_exr2vips, filename )) ) - return( NULL ); - } - else if( im_isppm( name ) ) { - if( !(im = open_sub( - im_ppm2vips_header, im_ppm2vips, filename )) ) - return( NULL ); - } - else if( im_ispng( name ) ) { - if( !(im = open_sub( - im_png2vips_header, im_png2vips, filename )) ) - return( NULL ); - } - else if( im_filename_suffix_match( name, im_suffix_csv ) ) { - if( !(im = open_sub( - im_csv2vips_header, im_csv2vips, filename )) ) - return( NULL ); - } - else if( im_isvips( name ) ) { - if( mode[1] == 'w' ) { - /* Has to be native format for >8 bits. - */ - if( !(im = im_init( filename )) ) - return( NULL ); - if( im_openinrw( im ) ) { - im_close( im ); - return( NULL ); - } - if( im->Bbits != IM_BBITS_BYTE && - im_isMSBfirst( im ) != - im_amiMSBfirst() ) { - im_close( im ); - im_error( "im_open", _( "open for read-" - "write for native format " - "images only" ) ); - return( NULL ); - } - } - else - im = read_vips( filename ); - } - else if( im_isanalyze( name ) ) { - if( !(im = open_sub( - im_analyze2vips_header, im_analyze2vips, - filename )) ) - return( NULL ); - } - else if( im_ismagick( name ) ) { - /* Have this last as it can be very slow to detect - * failure. - */ - if( !(im = open_sub( - im_magick2vips_header, im_magick2vips, - filename )) ) - return( NULL ); - } - else { - im_error( "im_open", _( "\"%s\" is not " - "a supported format" ), filename ); + if( !(im = open_sub( + format->header, format->load, filename )) ) return( NULL ); - } -} - break; case 'w': diff --git a/libsrc/iofuncs/package.c b/libsrc/iofuncs/package.c index 1d92dec3..28792c63 100644 --- a/libsrc/iofuncs/package.c +++ b/libsrc/iofuncs/package.c @@ -72,6 +72,8 @@ extern im_package im__other; extern im_package im__relational; extern im_package im__video; +extern im_format_package im__format_format; + /* im_guess_prefix() args. */ static im_arg_desc guess_prefix_args[] = { @@ -419,12 +421,56 @@ static im_package *built_in[] = { &im__video }; +/* List of loaded formats. + */ +static GSList *format_list = NULL; + +static gint +format_compare( im_format *a, im_format *b ) +{ + return( a->priority - b->priority ); +} + +/* Sort the format list after a change. + */ +static void +format_sort( void ) +{ + format_list = g_slist_sort( format_list, + (GCompareFunc) format_compare ); +} + +/* Remove a package of formats. + */ +static void +format_remove( im_format_package *format ) +{ + int i; + + for( i = 0; i < format->nfuncs; i++ ) + format_list = g_slist_remove( format_list, format->table[i] ); + format_sort(); +} + +/* Add a package of formats. + */ +static void +format_add( im_format_package *format ) +{ + int i; + + for( i = 0; i < format->nfuncs; i++ ) + format_list = g_slist_prepend( format_list, format->table[i] ); + format_sort(); +} + /* How we represent a loaded plugin. */ typedef struct _Plugin { GModule *module; /* As loaded by g_module_open() */ char *name; /* Name we loaded */ im_package *pack; /* Package table */ + im_format_package *format; /* Package format table */ } Plugin; /* List of loaded plugins. @@ -438,6 +484,8 @@ plugin_free( Plugin *plug ) { char *name = plug->name ? plug->name : ""; + if( plug->format ) + format_remove( plug->format ); if( plug->module ) { if( !g_module_close( plug->module ) ) { im_error( "plugin", @@ -477,6 +525,7 @@ im_load_plugin( const char *name ) plug->module = NULL; plug->name = NULL; plug->pack = NULL; + plug->format = NULL; plugin_list = g_slist_prepend( plugin_list, plug ); /* Attach name. @@ -511,22 +560,44 @@ im_load_plugin( const char *name ) return( NULL ); } + /* The format table is optional. + */ + if( !g_module_symbol( plug->module, + "format_table", (gpointer *) ((void *) &plug->format) ) ) + plug->format = NULL; + /* Sanity check. */ if( !plug->pack->name || plug->pack->nfuncs < 0 || plug->pack->nfuncs > 10000 ) { im_error( "plugin", - _( "corrupted package table in plugin \"%s\"" ), - name ); + _( "corrupted package table in plugin \"%s\"" ), name ); plugin_free( plug ); return( NULL ); } + if( plug->format ) { + if( !plug->format->name || plug->format->nfuncs < 0 || + plug->format->nfuncs > 10000 ) { + + im_error( "plugin", + _( "corrupted format table in plugin \"%s\"" ), + name ); + plugin_free( plug ); + + return( NULL ); + } + } #ifdef DEBUG printf( "added package \"%s\" ...\n", plug->pack->name ); #endif /*DEBUG*/ + /* Add any formats to our format list and sort again. + */ + if( plug->format ) + format_add( plug->format ); + return( plug->pack ); } @@ -700,6 +771,89 @@ im_package_of_function( const char *name ) return( pack ); } +/* Map a function over all formats. + */ +void * +im_map_formats( VSListMap2Fn fn, void *a, void *b ) +{ + return( im_slist_map2( format_list, fn, a, b ) ); +} + +/* Can this format open this file? + */ +static void * +format_for_file_sub( im_format *format, + const char *filename, const char *name ) +{ + if( format->is_a ) { + if( format->is_a( name ) ) + return( format ); + } + else if( im_filename_suffix_match( name, format->suffs ) ) + return( format ); + + return( NULL ); +} + +im_format * +im_format_for_file( const char *filename ) +{ + char name[FILENAME_MAX]; + char options[FILENAME_MAX]; + im_format *format; + + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( filename, name, options ); + + if( !im_existsf( "%s", name ) ) { + im_error( "im_format_for_file", + _( "\"%s\" is not readable" ), name ); + return( NULL ); + } + + format = (im_format *) im_map_formats( + (VSListMap2Fn) format_for_file_sub, + (void *) filename, (void *) name ); + + if( !format ) { + im_error( "im_format_for_file", + _( "\"%s\" is not in a supported format" ), name ); + return( NULL ); + } + + return( format ); +} + +/* Can we write this filename with this format? + */ +static void * +format_for_name_sub( im_format *format, + const char *filename, const char *name ) +{ + if( im_filename_suffix_match( name, format->suffs ) ) + return( format ); + + return( NULL ); +} + +im_format * +im_format_for_name( const char *filename ) +{ + char name[FILENAME_MAX]; + char options[FILENAME_MAX]; + + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( filename, name, options ); + + return( (im_format *) im_map_formats( + (VSListMap2Fn) format_for_name_sub, + (void *) filename, (void *) name ) ); +} + /* Free any store we allocated for the argument list. */ int