diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c index b38fc281..24c02849 100644 --- a/libvips/foreign/ppmload.c +++ b/libvips/foreign/ppmload.c @@ -64,7 +64,7 @@ /* TODO * - * - load filename streams with mmap ... we'll need to add get_flags_stream + * - load filename streams with mmap */ /* @@ -330,22 +330,25 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) static VipsForeignFlags vips_foreign_load_ppm_get_flags( VipsForeignLoad *load ) { + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; + VipsForeignFlags flags; flags = 0; - /* reenable this when mmap load goes back in - * - VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; - + /* If this streami supports fast mmap and this PPM is >=8 bit binary, + * then we can mmap the file and support partial load. Otherwise, + * it's sequential. + */ if( !ppm->have_read_header && vips_foreign_load_ppm_parse_header( ppm ) ) return( 0 ); - if( !ppm->ascii && ppm->bits >= 8 ) + if( vips_streami_is_mappable( ppm->streami ) && + !ppm->ascii && + ppm->bits >= 8 ) flags |= VIPS_FOREIGN_PARTIAL; - */ - - flags |= VIPS_FOREIGN_SEQUENTIAL; + else + flags |= VIPS_FOREIGN_SEQUENTIAL; return( flags ); } @@ -388,7 +391,9 @@ vips_foreign_load_ppm_header( VipsForeignLoad *load ) } /* Read a ppm/pgm file using mmap(). -load_mmap( VipsForeignLoadPpm *ppm, VipsImage *image ) + */ +static int +vips_foreign_load_ppm_map( VipsForeignLoadPpm *ppm, VipsImage *image ) { VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( ppm ), 3 ); @@ -400,7 +405,8 @@ load_mmap( VipsForeignLoadPpm *ppm, VipsImage *image ) vips_streamib_unbuffer( ppm->streamib ); header_offset = vips_streami_seek( ppm->streami, 0, SEEK_CUR ); data = vips_streami_map( ppm->streami, &length ); - if( header_offset < 0 || !data ) + if( header_offset < 0 || + !data ) return( -1 ); data += header_offset; length -= header_offset; @@ -416,7 +422,6 @@ load_mmap( VipsForeignLoadPpm *ppm, VipsImage *image ) return( 0 ); } - */ static int vips_foreign_load_ppm_generate_binary( VipsRegion *or, @@ -556,34 +561,46 @@ vips_foreign_load_ppm_load( VipsForeignLoad *load ) VipsImage **t = (VipsImage **) vips_object_local_array( (VipsObject *) load, 2 ); - VipsGenerateFn generate; - if( !ppm->have_read_header && vips_foreign_load_ppm_parse_header( ppm ) ) return( 0 ); - /* What sort of read are we doing? + /* If the stream is mappable and this is a binary file, we can map it. */ - if( !ppm->ascii && ppm->bits >= 8 ) { - generate = vips_foreign_load_ppm_generate_binary; - - /* The binary loader does not use the buffered IO object. - */ - vips_streamib_unbuffer( ppm->streamib ); + if( vips_streami_is_mappable( ppm->streami ) && + !ppm->ascii && + ppm->bits >= 8 ) { + if( vips_foreign_load_ppm_map( ppm, load->real ) ) + return( -1 ); } - else if( !ppm->ascii && ppm->bits == 1 ) - generate = vips_foreign_load_ppm_generate_1bit_binary; - else if( ppm->ascii && ppm->bits == 1 ) - generate = vips_foreign_load_ppm_generate_1bit_ascii; - else - generate = vips_foreign_load_ppm_generate_ascii_int; + else { + VipsGenerateFn generate; - t[0] = vips_image_new(); - vips_foreign_load_ppm_set_image( ppm, t[0] ); - if( vips_image_generate( t[0], NULL, generate, NULL, ppm, NULL ) || - vips_sequential( t[0], &t[1], NULL ) || - vips_image_write( t[1], load->real ) ) - return( -1 ); + /* What sort of read are we doing? + */ + if( !ppm->ascii && ppm->bits >= 8 ) { + generate = vips_foreign_load_ppm_generate_binary; + + /* The binary loader does not use the buffered IO + * object. + */ + vips_streamib_unbuffer( ppm->streamib ); + } + else if( !ppm->ascii && ppm->bits == 1 ) + generate = vips_foreign_load_ppm_generate_1bit_binary; + else if( ppm->ascii && ppm->bits == 1 ) + generate = vips_foreign_load_ppm_generate_1bit_ascii; + else + generate = vips_foreign_load_ppm_generate_ascii_int; + + t[0] = vips_image_new(); + vips_foreign_load_ppm_set_image( ppm, t[0] ); + if( vips_image_generate( t[0], + NULL, generate, NULL, ppm, NULL ) || + vips_sequential( t[0], &t[1], NULL ) || + vips_image_write( t[1], load->real ) ) + return( -1 ); + } if( vips_streami_decode( ppm->streami ) ) return( -1 ); diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index a585a851..4ce3d451 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -197,7 +197,8 @@ typedef struct _VipsForeignLoadClass { * of flags. If you don't define it, vips will default to 0 (no flags * set). * - * This operation is necessary for vips7 compatibility. + * This method is necessary for vips7 compatibility. Don't define + * it if you don't need vips7. */ VipsForeignFlags (*get_flags_filename)( const char *filename ); @@ -217,8 +218,7 @@ typedef struct _VipsForeignLoadClass { * @header() needs to set the dhint on the image .. otherwise you get * the default SMALLTILE. * - * Return 0 for success, -1 for error, setting - * vips_error(). + * Return 0 for success, -1 for error, setting vips_error(). */ int (*header)( VipsForeignLoad *load ); diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 533f13c6..ee2d1855 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -209,6 +209,7 @@ int vips_streami_unminimise( VipsStreami *streami ); int vips_streami_decode( VipsStreami *streami ); ssize_t vips_streami_read( VipsStreami *streami, void *data, size_t length ); const void *vips_streami_map( VipsStreami *streami, size_t *length ); +gboolean vips_streami_is_mappable( VipsStreami *streami ); gint64 vips_streami_seek( VipsStreami *streami, gint64 offset, int whence ); int vips_streami_rewind( VipsStreami *streami ); size_t vips_streami_sniff_at_most( VipsStreami *streami, diff --git a/libvips/iofuncs/streami.c b/libvips/iofuncs/streami.c index 8f5760ac..c0fa9da9 100644 --- a/libvips/iofuncs/streami.c +++ b/libvips/iofuncs/streami.c @@ -817,10 +817,11 @@ vips_streami_descriptor_to_memory( VipsStreami *streami ) * @streami: input stream to operate on * @length_out: return the file length here, or NULL * - * Map the stream object entirely into memory, and return a pointer to the + * Map the stream object entirely into memory and return a pointer to the * start. If @length_out is non-NULL, the file size if written to it. * - * This operation can take a long time. + * This operation can take a long time. Use vips_streami_is_mappable() to + * check if a streami can be mapped efficiently. * * Returns: a pointer to the start of the file contents, or NULL on error. */ @@ -860,6 +861,30 @@ vips_streami_map( VipsStreami *streami, size_t *length_out ) return( streami->data ); } +/** + * vips_streami_map: + * @streami: input stream to operate on + * @length_out: return the file length here, or NULL + * + * Map the stream object entirely into memory and return a pointer to the + * start. If @length_out is non-NULL, the file size if written to it. + * + * This operation can take a long time. Use vips_streami_is_mappable() to check + * if a streami can be mapped efficiently. + * + * Returns: a pointer to the start of the file contents, or NULL on error. + */ +gboolean +vips_streami_is_mappable( VipsStreami *streami ) +{ + /* Already a memory object, or there's a filename we can map, or + * there's a seekable descriptor. + */ + return( streami->data || + VIPS_STREAM( streami )->filename || + (streami->is_pipe && VIPS_STREAM( streami )->descriptor) ); +} + /** * vips_streami_seek: * @streami: input stream to operate on