diff --git a/ChangeLog b/ChangeLog index 278bc7a8..efd98ec4 100644 --- a/ChangeLog +++ b/ChangeLog @@ -58,6 +58,8 @@ - "header" is terse by default - "header" outputs filenames if working on many files - added openslide support (Benjamin Gilbert) +- allow new-style load/save options in filenames to + vips_image_new_from_file() etc. 12/10/11 started 7.26.6 - NOCACHE was not being set correctly on OS X causing performance diff --git a/TODO b/TODO index 7d82264b..7f1a87bb 100644 --- a/TODO +++ b/TODO @@ -1,35 +1,3 @@ -- we suffs on load as well for nip2's file-open dialog - - - - - - - - - - -- what should we do? - - vips_image_new_from_file( "x.tif[page=12]" ); - - or maybe: - - vips_image_new_from_file( "x.tif", "page", 12, NULL ); - - or maybe neither? we'd like this to work though: - - header x.tif[page=12] - - object_new_from_string()? - - - - - - - - - "header fred.png" does not work, since header uses im_open() which uses VipsForeign diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 0fc8c534..295d50f0 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -645,7 +645,7 @@ vips_foreign_save_print_class( VipsObjectClass *object_class, VipsBuf *buf ) /* Can we write this filename with this file? */ static void * -vips_foreign_save_new_from_foreignname_sub( VipsForeignSaveClass *save_class, +vips_foreign_find_save_sub( VipsForeignSaveClass *save_class, const char *filename ) { VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class ); @@ -674,7 +674,7 @@ vips_foreign_find_save( const char *filename ) if( !(save_class = (VipsForeignSaveClass *) vips_foreign_map( "VipsForeignSave", - (VipsSListMap2Fn) vips_foreign_save_new_from_foreignname_sub, + (VipsSListMap2Fn) vips_foreign_find_save_sub, (void *) filename, NULL )) ) { vips_error( "VipsForeignSave", _( "\"%s\" is not a supported image file." ), @@ -954,7 +954,7 @@ vips_foreign_save_init( VipsForeignSave *object ) * Loads @filename into @out using the loader recommended by * vips_foreign_find_load(). * - * See also: vips_foreign_write(). + * See also: vips_foreign_write(), vips_foreign_read_options(). * * Returns: 0 on success, -1 on error */ @@ -979,6 +979,7 @@ vips_foreign_read( const char *filename, VipsImage **out, ... ) * vips_foreign_write: * @in: image to write * @filename: file to write to + * @...: %NULL-terminated list of optional named arguments * * Saves @in to @filename using the saver recommended by * vips_foreign_find_save(). @@ -1004,6 +1005,95 @@ vips_foreign_write( VipsImage *in, const char *filename, ... ) return( result ); } +/** + * vips_foreign_read_options: + * @filename: file to load + * @out: output image + * + * Loads @filename into @out using the loader recommended by + * vips_foreign_find_load(). + * + * Arguments to the loader may be embedded in the filename using the usual + * syntax. + * + * See also: vips_foreign_read(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_foreign_read_options( const char *filename, VipsImage **out ) +{ + VipsObjectClass *oclass = g_type_class_ref( VIPS_TYPE_FOREIGN_LOAD ); + + VipsObject *object; + + /* This will use vips_foreign_load_new_from_string() to pick a loader, + * then set options from the remains of the string. + */ + if( !(object = vips_object_new_from_string( oclass, filename )) ) + return( -1 ); + + if( vips_cache_operation_build( (VipsOperation **) &object ) ) { + /* The build may have made some output objects before + * failing. + */ + vips_object_unref_outputs( object ); + g_object_unref( object ); + return( -1 ); + } + + g_object_get( object, "out", out, NULL ); + + /* Getting @out will have upped its count so it'll be safe. + * We can junk all other outputs, + */ + vips_object_unref_outputs( object ); + + /* @out holds a ref to new_object, we can drop ours. + */ + g_object_unref( object ); + + return( 0 ); +} + +/** + * vips_foreign_write_options: + * @in: image to write + * @filename: file to write to + * + * Saves @in to @filename using the saver recommended by + * vips_foreign_find_save(). + * + * See also: vips_foreign_write(). + * + * Returns: 0 on success, -1 on error + */ +int +vips_foreign_write_options( VipsImage *in, const char *filename ) +{ + VipsObjectClass *oclass = g_type_class_ref( VIPS_TYPE_FOREIGN_SAVE ); + VipsObject *object; + + /* This will use vips_foreign_save_new_from_string() to pick a saver, + * then set options from the tail of the filename. + */ + if( !(object = vips_object_new_from_string( oclass, filename )) ) + return( -1 ); + + g_object_set( object, "in", in, NULL ); + + /* ... and running _build() should save it. + */ + if( vips_cache_operation_build( (VipsOperation **) &object ) ) { + g_object_unref( object ); + return( -1 ); + } + + g_object_unref( object ); + + return( 0 ); +} + /* Called from iofuncs to init all operations in this dir. Use a plugin system * instead? */ diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 6f03b8d5..36757a40 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -233,6 +233,9 @@ int vips_foreign_read( const char *filename, VipsImage **out, ... ) int vips_foreign_write( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_foreign_read_options( const char *filename, VipsImage **out ); +int vips_foreign_write_options( VipsImage *in, const char *filename ); + void vips_foreign_operation_init( void ); int vips_openslideload( const char *filename, VipsImage **out, ... ) diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index d0767b2e..f9b9a5cd 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -466,23 +466,13 @@ vips_image_rewind( VipsObject *object ) * to a "p" and on "written" do im_vips2tiff() or whatever. */ -/* From "written" callback: invoke a delayed save. +/* From "written" callback: invoke a delayed save by building the write object.. */ static void -vips_image_save_cb( VipsImage *image, int *result, char *filename ) +vips_image_save_cb( VipsImage *image, int *result, VipsObject *write ) { - if( vips_foreign_write( image, filename, NULL ) ) + if( vips_object_build( write ) ) *result = -1; - - g_free( filename ); -} - -static void -vips_attach_save( VipsImage *image, const char *filename ) -{ - g_signal_connect( image, "written", - G_CALLBACK( vips_image_save_cb ), - g_strdup( filename ) ); } /* Progress feedback. @@ -627,7 +617,7 @@ vips_image_build( VipsObject *object ) else { VipsImage *t; - if( vips_foreign_read( filename, &t, NULL ) ) + if( vips_foreign_read_options( filename, &t ) ) return( -1 ); image->dtype = VIPS_IMAGE_PARTIAL; @@ -642,21 +632,36 @@ vips_image_build( VipsObject *object ) case 'w': { - const char *file_op; + VipsObjectClass *oclass = + g_type_class_ref( VIPS_TYPE_FOREIGN_SAVE ); + VipsObject *object; - if( !(file_op = vips_foreign_find_save( filename )) ) + /* This will use vips_foreign_save_new_from_string() to pick a + * saver, then set options from the tail of the filename. + */ + if( !(object = vips_object_new_from_string( oclass, + filename )) ) return( -1 ); + vips_object_local( image, object ); /* Make sure the vips saver is there ... strange things will * happen if that type is renamed or removed. */ g_assert( g_type_from_name( "VipsForeignSaveVips" ) ); - if( strcmp( file_op, "VipsForeignSaveVips" ) == 0 ) + /* If this ia the vips saver, just save directly ourselves. + * Otherwise trigger a _build() and save when the image has + * been written to. + */ + if( G_TYPE_CHECK_INSTANCE_TYPE( object, + g_type_from_name( "VipsForeignSaveVips" ) ) ) { image->dtype = VIPS_IMAGE_OPENOUT; + } else { image->dtype = VIPS_IMAGE_PARTIAL; - vips_attach_save( image, filename ); + g_signal_connect( image, "written", + G_CALLBACK( vips_image_save_cb ), + object ); } } break; diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index ab284dd0..a3c90443 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -1250,8 +1250,8 @@ vips_object_set_argument_from_string( VipsObject *object, GParamSpec *pspec; VipsArgumentClass *argument_class; VipsArgumentInstance *argument_instance; - GType otype; VipsObjectClass *oclass; + GType otype; GValue gvalue = { 0 }; @@ -1266,39 +1266,15 @@ vips_object_set_argument_from_string( VipsObject *object, g_assert( argument_class->flags & VIPS_ARGUMENT_INPUT ); - if( g_type_is_a( otype, VIPS_TYPE_IMAGE ) && - (oclass = g_type_class_ref( VIPS_TYPE_FOREIGN_LOAD )) ) { - VipsObject *new_object; + if( g_type_is_a( otype, VIPS_TYPE_IMAGE ) ) { VipsImage *out; - /* Use VipsForeign to build images, then pull @out from it and - * use that to set the value. + /* Read the filename. vips_foreign_read_options() + * handles embedded options. */ - if( !(new_object = - vips_object_new_from_string( oclass, value )) ) + if( vips_foreign_read_options( value, &out ) ) return( -1 ); - if( vips_cache_operation_build( - (VipsOperation **) &new_object ) ) { - /* The build may have made some output objects before - * failing. - */ - vips_object_unref_outputs( new_object ); - g_object_unref( new_object ); - return( -1 ); - } - - g_object_get( new_object, "out", &out, NULL ); - - /* Getting @out will have upped its count so it'll be safe. - * We can junk all other outputs, - */ - vips_object_unref_outputs( new_object ); - - /* @out holds a ref to new_object, we can drop ours. - */ - g_object_unref( new_object ); - g_value_init( &gvalue, VIPS_TYPE_IMAGE ); g_value_set_object( &gvalue, out ); @@ -1460,29 +1436,18 @@ vips_object_get_argument_to_string( VipsObject *object, g_assert( argument_class->flags & VIPS_ARGUMENT_OUTPUT ); - if( g_type_is_a( otype, VIPS_TYPE_IMAGE ) && - (oclass = g_type_class_ref( VIPS_TYPE_FOREIGN_SAVE )) ) { - VipsObject *new_object; + if( g_type_is_a( otype, VIPS_TYPE_IMAGE ) ) { VipsImage *in; - /* Use VipsForeign to make a saver, set 'in' on that and run - * build to make it write. + /* Pull out the image and write it. + * vips_foreign_write_options() handles embedded options. */ - if( !(new_object = - vips_object_new_from_string( oclass, arg )) ) - return( -1 ); - g_object_get( object, name, &in, NULL ); - g_object_set( new_object, "in", in, NULL ); - g_object_unref( in ); - - if( vips_cache_operation_build( - (VipsOperation **) &new_object ) ) { - g_object_unref( new_object ); + if( vips_foreign_write_options( in, arg ) ) { + g_object_unref( in ); return( -1 ); } - - g_object_unref( new_object ); + g_object_unref( in ); } else if( g_type_is_a( otype, VIPS_TYPE_OBJECT ) && (oclass = g_type_class_ref( otype )) &&