diff --git a/libvips/foreign/heifload.c b/libvips/foreign/heifload.c index acf495b5..29524ce4 100644 --- a/libvips/foreign/heifload.c +++ b/libvips/foreign/heifload.c @@ -152,37 +152,42 @@ typedef struct _VipsForeignLoadHeif { int stride; const uint8_t *data; + /* Set from subclasses. + */ + VipsSource *source; + + /* The reader struct. We use this to attach to our VipsSource. This + * has to be alloced rather than in our struct, since it may change + * size in libheif API versions. + */ + struct heif_reader *reader; + + /* When we see EOF from read(), record the source length here. + */ + gint64 length; + } VipsForeignLoadHeif; typedef struct _VipsForeignLoadHeifClass { VipsForeignLoadClass parent_class; - /* Open the reader, eg. call heif_context_read_from_memory() etc. This - * has to be a vfunc so generate can restart after minimise. - */ - int (*open)( VipsForeignLoadHeif *heif ); - } VipsForeignLoadHeifClass; G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadHeif, vips_foreign_load_heif, VIPS_TYPE_FOREIGN_LOAD ); -static void -vips_foreign_load_heif_close( VipsForeignLoadHeif *heif ) -{ - VIPS_FREEF( heif_image_release, heif->img ); - heif->data = NULL; - VIPS_FREEF( heif_image_handle_release, heif->handle ); - VIPS_FREEF( heif_context_free, heif->ctx ); -} - static void vips_foreign_load_heif_dispose( GObject *gobject ) { VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) gobject; - vips_foreign_load_heif_close( heif ); + heif->data = NULL; + VIPS_FREEF( heif_image_release, heif->img ); + VIPS_FREEF( heif_image_handle_release, heif->handle ); + VIPS_FREEF( heif_context_free, heif->ctx ); VIPS_FREE( heif->id ); + VIPS_FREE( heif->reader ); + VIPS_UNREF( heif->source ); G_OBJECT_CLASS( vips_foreign_load_heif_parent_class )-> dispose( gobject ); @@ -196,6 +201,31 @@ vips__heif_error( struct heif_error *error ) error->subcode ); } +static int +vips_foreign_load_heif_build( VipsObject *object ) +{ + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) object; + + if( !heif->ctx ) { + struct heif_error error; + + heif->ctx = heif_context_alloc(); + error = heif_context_read_from_reader( heif->ctx, + heif->reader, heif, NULL ); + if( error.code ) { + vips__heif_error( &error ); + return( -1 ); + } + } + + if( VIPS_OBJECT_CLASS( vips_foreign_load_heif_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + + static const char *heif_magic[] = { "ftypheic", /* A regular heif image */ "ftypheix", /* Extended range (>8 bit) image */ @@ -308,6 +338,8 @@ vips_foreign_load_heif_set_page( VipsForeignLoadHeif *heif, static int vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) { + VipsForeignLoad *load = (VipsForeignLoad *) heif; + int bands; int i; /* Surely, 16 metadata items will be enough for anyone. @@ -464,6 +496,9 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out ) VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, VIPS_INTERPRETATION_sRGB, 1.0, 1.0 ); + VIPS_SETSTR( load->out->filename, + vips_connection_filename( VIPS_CONNECTION( heif->source ) ) ); + return( 0 ); } @@ -506,16 +541,11 @@ vips_foreign_load_heif_header( VipsForeignLoad *load ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load ); VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) load; - VipsForeignLoadHeifClass *heif_class = - VIPS_FOREIGN_LOAD_HEIF_GET_CLASS( heif ); struct heif_error error; heif_item_id primary_id; int i; - if( heif_class->open( heif ) ) - return( -1 ); - heif->n_top = heif_context_get_number_of_top_level_images( heif->ctx ); heif->id = VIPS_ARRAY( NULL, heif->n_top, heif_item_id ); heif_context_get_list_of_top_level_image_IDs( heif->ctx, @@ -645,7 +675,7 @@ vips_foreign_load_heif_header( VipsForeignLoad *load ) if( vips_foreign_load_heif_set_header( heif, load->out ) ) return( -1 ); - vips_foreign_load_heif_close( heif ); + vips_source_minimise( heif->source ); return( 0 ); } @@ -656,8 +686,6 @@ vips_foreign_load_heif_generate( VipsRegion *or, { VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) a; VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( heif ); - VipsForeignLoadHeifClass *heif_class = - VIPS_FOREIGN_LOAD_HEIF_GET_CLASS( heif ); VipsRect *r = &or->valid; int page = r->top / heif->page_height + heif->page; @@ -669,9 +697,6 @@ vips_foreign_load_heif_generate( VipsRegion *or, g_assert( r->height == 1 ); - if( heif_class->open( heif ) ) - return( -1 ); - if( vips_foreign_load_heif_set_page( heif, page, heif->thumbnail ) ) return( -1 ); @@ -779,15 +804,13 @@ vips_foreign_load_heif_generate( VipsRegion *or, static void vips_foreign_load_heif_minimise( VipsObject *object, VipsForeignLoadHeif *heif ) { - vips_foreign_load_heif_close( heif ); + vips_source_minimise( heif->source ); } static int vips_foreign_load_heif_load( VipsForeignLoad *load ) { VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) load; - VipsForeignLoadHeifClass *class = - VIPS_FOREIGN_LOAD_HEIF_GET_CLASS( heif ); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( load ), 3 ); @@ -796,9 +819,6 @@ vips_foreign_load_heif_load( VipsForeignLoad *load ) printf( "vips_foreign_load_heif_load: loading image\n" ); #endif /*DEBUG*/ - if( class->open( heif ) ) - return( -1 ); - t[0] = vips_image_new(); if( vips_foreign_load_heif_set_header( heif, t[0] ) ) return( -1 ); @@ -817,20 +837,12 @@ vips_foreign_load_heif_load( VipsForeignLoad *load ) return( 0 ); } -static int -vips_foreign_load_heif_open( VipsForeignLoadHeif *heif ) -{ - return( 0 ); -} - static void vips_foreign_load_heif_class_init( VipsForeignLoadHeifClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; - VipsForeignLoadHeifClass *heif_class = - (VipsForeignLoadHeifClass *) class; gobject_class->dispose = vips_foreign_load_heif_dispose; gobject_class->set_property = vips_object_set_property; @@ -838,13 +850,12 @@ vips_foreign_load_heif_class_init( VipsForeignLoadHeifClass *class ) object_class->nickname = "heifload_base"; object_class->description = _( "load a HEIF image" ); + object_class->build = vips_foreign_load_heif_build; load_class->get_flags = vips_foreign_load_heif_get_flags; load_class->header = vips_foreign_load_heif_header; load_class->load = vips_foreign_load_heif_load; - heif_class->open = vips_foreign_load_heif_open; - VIPS_ARG_INT( class, "page", 2, _( "Page" ), _( "Load this page from the file" ), @@ -875,10 +886,77 @@ vips_foreign_load_heif_class_init( VipsForeignLoadHeifClass *class ) } +static gint64 +vips_foreign_load_heif_get_position( void *userdata ) +{ + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) userdata; + + return( vips_source_seek( heif->source, 0L, SEEK_CUR ) ); +} + +static int +vips_foreign_load_heif_read( void *data, size_t size, void *userdata ) +{ + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) userdata; + + int result; + + result = vips_source_read( heif->source, data, size ); + if( result == 0 ) { + printf( "vips_foreign_load_heif_read: seen EOF\n" ); + heif->length = vips_source_seek( heif->source, 0L, SEEK_CUR ); + } + if( result < 0 ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_heif_seek( gint64 position, void *userdata ) +{ + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) userdata; + + vips_source_seek( heif->source, position, SEEK_SET ); + + return( 0 ); +} + +static enum heif_reader_grow_status +vips_foreign_load_heif_wait_for_file_size( gint64 target_size, void *userdata ) +{ + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) userdata; + + return( heif_reader_grow_status_size_reached ); + + if( heif->length == -1 || + target_size < heif->length ) + /* We've not seen EOF yet (so seeking to any point is fine), OR + * we've seen EOF, and this target is less than that. + */ + return( heif_reader_grow_status_size_reached ); + else + /* We've seen EOF, we know the length, and this is too far. + */ + return( heif_reader_grow_status_size_beyond_eof ); +} + static void vips_foreign_load_heif_init( VipsForeignLoadHeif *heif ) { heif->n = 1; + heif->length = -1; + + heif->reader = VIPS_ARRAY( NULL, 1, struct heif_reader ); + + /* The first version to support heif_reader. + */ + heif->reader->reader_api_version = 1.3; + heif->reader->get_position = vips_foreign_load_heif_get_position; + heif->reader->read = vips_foreign_load_heif_read; + heif->reader->seek = vips_foreign_load_heif_seek; + heif->reader->wait_for_file_size = + vips_foreign_load_heif_wait_for_file_size; } typedef struct _VipsForeignLoadHeifFile { @@ -895,6 +973,24 @@ typedef VipsForeignLoadHeifClass VipsForeignLoadHeifFileClass; G_DEFINE_TYPE( VipsForeignLoadHeifFile, vips_foreign_load_heif_file, vips_foreign_load_heif_get_type() ); +static int +vips_foreign_load_heif_file_build( VipsObject *object ) +{ + VipsForeignLoadHeifFile *file = (VipsForeignLoadHeifFile *) object; + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) object; + + if( file->filename ) + if( !(heif->source = + vips_source_new_from_file( file->filename )) ) + return( -1 ); + + if( VIPS_OBJECT_CLASS( vips_foreign_load_heif_file_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + static int vips_foreign_load_heif_file_is_a( const char *filename ) { @@ -906,25 +1002,6 @@ vips_foreign_load_heif_file_is_a( const char *filename ) return( vips_foreign_load_heif_is_a( buf, 12 ) ); } -static int -vips_foreign_load_heif_file_header( VipsForeignLoad *load ) -{ - VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) load; - VipsForeignLoadHeifFile *file = (VipsForeignLoadHeifFile *) load; - - if( VIPS_FOREIGN_LOAD_CLASS( - vips_foreign_load_heif_file_parent_class )->header( load ) ) { - /* Close early if our base class fails to read. - */ - vips_foreign_load_heif_close( heif ); - return( -1 ); - } - - VIPS_SETSTR( load->out->filename, file->filename ); - - return( 0 ); -} - const char *vips__heif_suffs[] = { ".heic", ".heif", @@ -932,31 +1009,6 @@ const char *vips__heif_suffs[] = { NULL }; -static int -vips_foreign_load_heif_file_open( VipsForeignLoadHeif *heif ) -{ - VipsForeignLoadHeifFile *file = (VipsForeignLoadHeifFile *) heif; - - if( !heif->ctx ) { - struct heif_error error; - - heif->ctx = heif_context_alloc(); - - error = heif_context_read_from_file( heif->ctx, - file->filename, NULL ); - if( error.code ) { - /* Make we close the fd as soon as we can on error. - */ - vips_foreign_load_heif_close( heif ); - vips__heif_error( &error ); - return( -1 ); - } - } - - return( VIPS_FOREIGN_LOAD_HEIF_CLASS( - vips_foreign_load_heif_file_parent_class )->open( heif ) ); -} - static void vips_foreign_load_heif_file_class_init( VipsForeignLoadHeifFileClass *class ) { @@ -964,20 +1016,16 @@ vips_foreign_load_heif_file_class_init( VipsForeignLoadHeifFileClass *class ) VipsObjectClass *object_class = (VipsObjectClass *) class; VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; - VipsForeignLoadHeifClass *heif_class = - (VipsForeignLoadHeifClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; object_class->nickname = "heifload"; + object_class->build = vips_foreign_load_heif_file_build; foreign_class->suffs = vips__heif_suffs; load_class->is_a = vips_foreign_load_heif_file_is_a; - load_class->header = vips_foreign_load_heif_file_header; - - heif_class->open = vips_foreign_load_heif_file_open; VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), @@ -1007,35 +1055,32 @@ typedef VipsForeignLoadHeifClass VipsForeignLoadHeifBufferClass; G_DEFINE_TYPE( VipsForeignLoadHeifBuffer, vips_foreign_load_heif_buffer, vips_foreign_load_heif_get_type() ); +static int +vips_foreign_load_heif_buffer_build( VipsObject *object ) +{ + VipsForeignLoadHeifBuffer *buffer = + (VipsForeignLoadHeifBuffer *) object; + VipsForeignLoadHeif *heif = (VipsForeignLoadHeif *) object; + + if( buffer->buf ) + if( !(heif->source = vips_source_new_from_memory( + VIPS_AREA( buffer->buf )->data, + VIPS_AREA( buffer->buf )->length )) ) + return( -1 ); + + if( VIPS_OBJECT_CLASS( vips_foreign_load_heif_file_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + static gboolean vips_foreign_load_heif_buffer_is_a( const void *buf, size_t len ) { return( vips_foreign_load_heif_is_a( buf, len ) ); } -static int -vips_foreign_load_heif_buffer_open( VipsForeignLoadHeif *heif ) -{ - VipsForeignLoadHeifBuffer *buffer = (VipsForeignLoadHeifBuffer *) heif; - - VIPS_DEBUG_MSG( "vips_foreign_load_heif_buffer_open:\n" ); - - if( !heif->ctx ) { - struct heif_error error; - - heif->ctx = heif_context_alloc(); - error = heif_context_read_from_memory( heif->ctx, - buffer->buf->data, buffer->buf->length, NULL ); - if( error.code ) { - vips__heif_error( &error ); - return( -1 ); - } - } - - return( VIPS_FOREIGN_LOAD_HEIF_CLASS( - vips_foreign_load_heif_buffer_parent_class )->open( heif ) ); -} - static void vips_foreign_load_heif_buffer_class_init( VipsForeignLoadHeifBufferClass *class ) @@ -1043,18 +1088,15 @@ vips_foreign_load_heif_buffer_class_init( GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; - VipsForeignLoadHeifClass *heif_class = - (VipsForeignLoadHeifClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; object_class->nickname = "heifload_buffer"; + object_class->build = vips_foreign_load_heif_buffer_build; load_class->is_a_buffer = vips_foreign_load_heif_buffer_is_a; - heif_class->open = vips_foreign_load_heif_buffer_open; - VIPS_ARG_BOXED( class, "buffer", 1, _( "Buffer" ), _( "Buffer to load from" ),