diff --git a/ChangeLog b/ChangeLog index 45a4ef5d..a831042c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,7 +10,7 @@ - add _source load support for pdfium - add "seed" param to perlin, worley and gaussnoise - add vips_source_g_input_stream_new() to load images from a GInputStream -- add openslideload_source() +- add openslideload_source(), vipsload_source() 22/12/20 start 8.10.6 - don't seek on bad file descriptors [kleisauke] diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index d0722a33..fb4df9c8 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -424,6 +424,18 @@ vips_foreign_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) (VipsClassMapFn) file_add_class, (void *) &files ); files = g_slist_sort( files, (GCompareFunc) file_compare ); +#ifdef DEBUG +{ + GSList *p; + + printf( "vips_foreign_map: search order\n" ); + for( p = files; p; p = p->next ) { + VipsForeignClass *class = (VipsForeignClass *) p->data; + + printf( "\t%s\n", VIPS_OBJECT_CLASS( class )->nickname ); + } +} +#endif /*DEBUG*/ result = vips_slist_map2( files, fn, a, b ); g_slist_free( files ); @@ -2077,9 +2089,13 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_openexr_get_type( void ); - extern GType vips_foreign_load_openslide_get_type( void ); + extern GType vips_foreign_load_openslide_file_get_type( void ); extern GType vips_foreign_load_openslide_source_get_type( void ); + extern GType vips_foreign_load_vips_file_get_type( void ); + extern GType vips_foreign_load_vips_source_get_type( void ); + extern GType vips_foreign_save_vips_get_type( void ); + extern GType vips_foreign_load_jpeg_file_get_type( void ); extern GType vips_foreign_load_jpeg_buffer_get_type( void ); extern GType vips_foreign_load_jpeg_source_get_type( void ); @@ -2094,9 +2110,6 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_tiff_file_get_type( void ); extern GType vips_foreign_save_tiff_buffer_get_type( void ); - extern GType vips_foreign_load_vips_get_type( void ); - extern GType vips_foreign_save_vips_get_type( void ); - extern GType vips_foreign_load_raw_get_type( void ); extern GType vips_foreign_save_raw_get_type( void ); extern GType vips_foreign_save_raw_fd_get_type( void ); @@ -2152,7 +2165,8 @@ vips_foreign_operation_init( void ) vips_foreign_load_raw_get_type(); vips_foreign_save_raw_get_type(); vips_foreign_save_raw_fd_get_type(); - vips_foreign_load_vips_get_type(); + vips_foreign_load_vips_file_get_type(); + vips_foreign_load_vips_source_get_type(); vips_foreign_save_vips_get_type(); #ifdef HAVE_ANALYZE @@ -2251,7 +2265,7 @@ vips_foreign_operation_init( void ) #endif /*HAVE_TIFF*/ #ifdef HAVE_OPENSLIDE - vips_foreign_load_openslide_get_type(); + vips_foreign_load_openslide_file_get_type(); vips_foreign_load_openslide_source_get_type(); #endif /*HAVE_OPENSLIDE*/ diff --git a/libvips/foreign/openslideload.c b/libvips/foreign/openslideload.c index 0d2b678e..0338bb21 100644 --- a/libvips/foreign/openslideload.c +++ b/libvips/foreign/openslideload.c @@ -114,21 +114,24 @@ vips_foreign_load_openslide_build( VipsObject *object ) VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) object; - if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )-> - build( object ) ) - return( -1 ); - /* We can only open source which have an associated filename, since * the openslide library works in terms of filenames. */ - openslide->filename = vips_connection_filename( VIPS_CONNECTION( - openslide->source ) ); - if( !openslide->filename ) { - vips_error( class->nickname, "%s", - _( "no filename available" ) ); - return( -1 ); + if( openslide->source ) { + openslide->filename = + vips_connection_filename( VIPS_CONNECTION( + openslide->source ) ); + if( !openslide->filename ) { + vips_error( class->nickname, "%s", + _( "no filename available" ) ); + return( -1 ); + } } + if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )-> + build( object ) ) + return( -1 ); + return( 0 ); } diff --git a/libvips/foreign/vipsload.c b/libvips/foreign/vipsload.c index c283adc0..081a8579 100644 --- a/libvips/foreign/vipsload.c +++ b/libvips/foreign/vipsload.c @@ -48,29 +48,38 @@ typedef struct _VipsForeignLoadVips { VipsForeignLoad parent_object; - char *filename; + /* Source to load from (set by subclasses). + */ + VipsSource *source; } VipsForeignLoadVips; typedef VipsForeignLoadClass VipsForeignLoadVipsClass; -G_DEFINE_TYPE( VipsForeignLoadVips, vips_foreign_load_vips, +G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadVips, vips_foreign_load_vips, VIPS_TYPE_FOREIGN_LOAD ); -static gboolean -vips_foreign_load_vips_is_a( const char *filename ) +static void +vips_foreign_load_vips_dispose( GObject *gobject ) { - return( vips__file_magic( filename ) ); + VipsForeignLoadVips *vips = (VipsForeignLoadVips *) gobject; + + VIPS_UNREF( vips->source ); + + G_OBJECT_CLASS( vips_foreign_load_vips_parent_class )-> + dispose( gobject ); } static VipsForeignFlags -vips_foreign_load_vips_get_flags_filename( const char *filename ) +vips_foreign_load_vips_get_flags_source( VipsSource *source ) { + unsigned char *data; VipsForeignFlags flags; flags = VIPS_FOREIGN_PARTIAL; - if( vips__file_magic( filename ) == VIPS_MAGIC_SPARC ) + if( vips_source_sniff_at_most( source, &data, 4 ) == 4 && + *((guint32 *) data) == VIPS_MAGIC_SPARC ) flags |= VIPS_FOREIGN_BIGENDIAN; return( flags ); @@ -81,34 +90,120 @@ vips_foreign_load_vips_get_flags( VipsForeignLoad *load ) { VipsForeignLoadVips *vips = (VipsForeignLoadVips *) load; - return( vips_foreign_load_vips_get_flags_filename( vips->filename ) ); + return( vips_foreign_load_vips_get_flags_source( vips->source ) ); +} + +static VipsForeignFlags +vips_foreign_load_vips_get_flags_filename( const char *filename ) +{ + VipsSource *source; + VipsForeignFlags flags; + + if( !(source = vips_source_new_from_file( filename )) ) + return( 0 ); + flags = vips_foreign_load_vips_get_flags_source( source ); + VIPS_UNREF( source ); + + return( flags ); } static int vips_foreign_load_vips_header( VipsForeignLoad *load ) { VipsForeignLoadVips *vips = (VipsForeignLoadVips *) load; - VipsImage *out; - VipsImage *out2; - if( !(out2 = vips_image_new_mode( vips->filename, "r" )) ) + const char *filename; + VipsImage *image; + + if( (filename = + vips_connection_filename( VIPS_CONNECTION( vips->source ) )) ) { + if( !(image = vips_image_new_mode( filename, "r" )) ) + return( -1 ); + } + else { + /* We could add load vips from memory, fd, via mmap etc. here. + * We should perhaps move iofuncs/vips.c into this file. + * + * For now, just fail unless there's a filename associated + * with this source. + */ + return( -1 ); + } + + g_object_set( load, "out", image, NULL ); + + return( 0 ); +} + +static void +vips_foreign_load_vips_class_init( VipsForeignLoadVipsClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->dispose = vips_foreign_load_vips_dispose; + + object_class->nickname = "vipsload_base"; + object_class->description = _( "load vips base class" ); + + /* We are fast at is_a(), so high priority. + */ + foreign_class->priority = 200; + + load_class->get_flags = vips_foreign_load_vips_get_flags; + load_class->get_flags_filename = + vips_foreign_load_vips_get_flags_filename; + load_class->header = vips_foreign_load_vips_header; + load_class->load = NULL; + +} + +static void +vips_foreign_load_vips_init( VipsForeignLoadVips *vips ) +{ +} + +typedef struct _VipsForeignLoadVipsFile { + VipsForeignLoadVips parent_object; + + char *filename; + +} VipsForeignLoadVipsFile; + +typedef VipsForeignLoadVipsClass VipsForeignLoadVipsFileClass; + +G_DEFINE_TYPE( VipsForeignLoadVipsFile, vips_foreign_load_vips_file, + vips_foreign_load_vips_get_type() ); + +static int +vips_foreign_load_vips_file_build( VipsObject *object ) +{ + VipsForeignLoadVips *vips = (VipsForeignLoadVips *) object; + VipsForeignLoadVipsFile *file = (VipsForeignLoadVipsFile *) object; + + if( file->filename && + !(vips->source = vips_source_new_from_file( file->filename )) ) return( -1 ); - /* Remove the @out that's there now. - */ - g_object_get( load, "out", &out, NULL ); - g_object_unref( out ); - g_object_unref( out ); - - g_object_set( load, "out", out2, NULL ); + if( VIPS_OBJECT_CLASS( vips_foreign_load_vips_file_parent_class )-> + build( object ) ) + return( -1 ); return( 0 ); } const char *vips__suffs[] = { ".v", ".vips", NULL }; +static gboolean +vips_foreign_load_vips_file_is_a( const char *filename ) +{ + return( vips__file_magic( filename ) ); +} + static void -vips_foreign_load_vips_class_init( VipsForeignLoadVipsClass *class ) +vips_foreign_load_vips_file_class_init( VipsForeignLoadVipsClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; @@ -120,33 +215,98 @@ vips_foreign_load_vips_class_init( VipsForeignLoadVipsClass *class ) object_class->nickname = "vipsload"; object_class->description = _( "load vips from file" ); + object_class->build = vips_foreign_load_vips_file_build; foreign_class->suffs = vips__suffs; - /* We are fast at is_a(), so high priority. - */ - foreign_class->priority = 200; - - load_class->is_a = vips_foreign_load_vips_is_a; - load_class->get_flags = vips_foreign_load_vips_get_flags; - load_class->get_flags_filename = - vips_foreign_load_vips_get_flags_filename; - load_class->header = vips_foreign_load_vips_header; - load_class->load = NULL; + load_class->is_a = vips_foreign_load_vips_file_is_a; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadVips, filename ), + G_STRUCT_OFFSET( VipsForeignLoadVipsFile, filename ), NULL ); } static void -vips_foreign_load_vips_init( VipsForeignLoadVips *vips ) +vips_foreign_load_vips_file_init( VipsForeignLoadVipsFile *file ) { } +typedef struct _VipsForeignLoadVipsSource { + VipsForeignLoadVips parent_object; + + VipsSource *source; + +} VipsForeignLoadVipsSource; + +typedef VipsForeignLoadVipsClass VipsForeignLoadVipsSourceClass; + +G_DEFINE_TYPE( VipsForeignLoadVipsSource, vips_foreign_load_vips_source, + vips_foreign_load_vips_get_type() ); + +static int +vips_foreign_load_vips_source_build( VipsObject *object ) +{ + VipsForeignLoadVips *vips = (VipsForeignLoadVips *) object; + VipsForeignLoadVipsSource *source = + (VipsForeignLoadVipsSource *) object; + + if( source->source ) { + vips->source = source->source; + g_object_ref( vips->source ); + } + + if( VIPS_OBJECT_CLASS( vips_foreign_load_vips_source_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static gboolean +vips_foreign_load_vips_source_is_a_source( VipsSource *source ) +{ + const char *filename; + + return( (filename = + vips_connection_filename( VIPS_CONNECTION( source ) )) && + vips__file_magic( filename ) ); +} + +static void +vips_foreign_load_vips_source_class_init( VipsForeignLoadVipsClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "vipsload_source"; + object_class->description = _( "load vips from source" ); + object_class->build = vips_foreign_load_vips_source_build; + + load_class->is_a_source = + vips_foreign_load_vips_source_is_a_source; + + VIPS_ARG_OBJECT( class, "source", 1, + _( "Source" ), + _( "Source to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadVipsSource, source ), + VIPS_TYPE_SOURCE ); + +} + +static void +vips_foreign_load_vips_source_init( VipsForeignLoadVipsSource *source ) +{ +} + + /** * vips_vipsload: * @filename: file to load @@ -171,3 +331,26 @@ vips_vipsload( const char *filename, VipsImage **out, ... ) return( result ); } + +/** + * vips_vipsload_source: + * @source: source to load from + * @out: (out): decompressed image + * @...: %NULL-terminated list of optional named arguments + * + * Exactly as vips_vipsload(), but read from a source. + * + * Returns: 0 on success, -1 on error. + */ +int +vips_vipsload_source( VipsSource *source, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "vipsload_source", ap, source, out ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index e8439f70..6dfc279e 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -358,6 +358,8 @@ const char *vips_foreign_find_save_target( const char *suffix ); int vips_vipsload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_vipsload_source( VipsSource *source, VipsImage **out, ... ) + __attribute__((sentinel)); int vips_vipssave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index fba9ec49..273f46fc 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -773,7 +773,7 @@ class TestForeign: source = pyvips.Source.new_from_file(OPENSLIDE_FILE) x = pyvips.Image.new_from_source(source, "") - assert openslide_valid(x) + openslide_valid(x) @skip_if_no("pdfload") def test_pdfload(self):