tiff reader supports new sequential mode

good speedup for large tiffs

$ time ~/vips-7.26/bin/vips-7.26 vips im_copy wtc.tif x.v
real	0m12.728s
user	0m0.220s
sys	0m1.032s

$ time vips copy wtc.tif[sequential] x.v
real	0m4.328s
user	0m0.584s
sys	0m0.764s

new one was compiled with DEBUG, hence (partly) larger user time
This commit is contained in:
John Cupitt 2012-02-18 13:17:21 +00:00
parent 77ab09d451
commit fa03bfb4cd
6 changed files with 127 additions and 71 deletions

View File

@ -7,6 +7,7 @@
- better im_shrink() - better im_shrink()
- added vips_sequential() - added vips_sequential()
- new vips_sink_memory() keeps read ordering - new vips_sink_memory() keeps read ordering
- tiff reader supports sequential read
20/8/11 started 7.27.0 20/8/11 started 7.27.0
- version bump for new dev cycle - version bump for new dev cycle

4
TODO
View File

@ -1,8 +1,10 @@
- add a sequential mode to all readers - add a sequential mode to jpeg reader
tiff, jpg are the obvious ones tiff, jpg are the obvious ones
- is the tif reader deadlocking sometimes?

View File

@ -125,6 +125,9 @@
* 5/12/11 * 5/12/11
* - make into a simple function call ready to be wrapped as a new-style * - make into a simple function call ready to be wrapped as a new-style
* VipsForeign class * VipsForeign class
* 18/2/12
* - switch to sequential read
* - remove the lock ... tilecache does this for us
*/ */
/* /*
@ -209,10 +212,10 @@ typedef struct {
/* Geometry. /* Geometry.
*/ */
uint32 twidth, theight; /* Tile size */ uint32 twidth, theight; /* Tile size */
uint32 rows_per_strip;
/* Only need one of these, since we mutex around TIFF*(). tsize_t scanline_size;
*/ tsize_t strip_size;
GMutex *tlock; /* Lock for TIFF*() calls */ int number_of_strips;
} ReadTiff; } ReadTiff;
/* Handle TIFF errors here. Shared with vips2tiff.c. These can be called from /* Handle TIFF errors here. Shared with vips2tiff.c. These can be called from
@ -1131,14 +1134,11 @@ tiff_fill_region_aligned( VipsRegion *out, void *seq, void *a, void *b )
/* Read that tile directly into the vips tile. /* Read that tile directly into the vips tile.
*/ */
g_mutex_lock( rtiff->tlock );
if( TIFFReadTile( rtiff->tiff, if( TIFFReadTile( rtiff->tiff,
VIPS_REGION_ADDR( out, r->left, r->top ), VIPS_REGION_ADDR( out, r->left, r->top ),
r->left, r->top, 0, 0 ) < 0 ) { r->left, r->top, 0, 0 ) < 0 ) {
g_mutex_unlock( rtiff->tlock );
return( -1 ); return( -1 );
} }
g_mutex_unlock( rtiff->tlock );
return( 0 ); return( 0 );
} }
@ -1189,12 +1189,8 @@ tiff_fill_region( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
/* Read that tile. /* Read that tile.
*/ */
g_mutex_lock( rtiff->tlock ); if( TIFFReadTile( rtiff->tiff, buf, x, y, 0, 0 ) < 0 )
if( TIFFReadTile( rtiff->tiff, buf, x, y, 0, 0 ) < 0 ) {
g_mutex_unlock( rtiff->tlock );
return( -1 ); return( -1 );
}
g_mutex_unlock( rtiff->tlock );
/* The tile we read. /* The tile we read.
*/ */
@ -1287,80 +1283,137 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out )
return( 0 ); return( 0 );
} }
/* Stripwise reading - we assume strips are written top-to-bottom. Not sure if static int
* this is always correct. tiff2vips_stripwise_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
ReadTiff *rtiff = (ReadTiff *) a;
tdata_t tbuf = (tdata_t) b;
VipsRect *r = &or->valid;
tdata_t dst;
tstrip_t strip;
tsize_t length;
#ifdef DEBUG
printf( "tiff2vips: read_stripwise_generate: top = %d, height = %d\n",
r->top, r->height );
#endif /*DEBUG*/
/* We're inside a tilecache where tiles are the full image width, so
* this should always be true.
*/
g_assert( r->left == 0 );
g_assert( r->width == or->im->Xsize );
g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize );
/* Tiles should always be on a strip boundary and exactly one strip
* high.
*/
g_assert( r->top % rtiff->rows_per_strip == 0 );
g_assert( r->height ==
VIPS_MIN( rtiff->rows_per_strip, or->im->Ysize - r->top ) );
/* Read directly into the image if we can. Otherwise, we must read
* to a temp buffer then unpack into the image.
*/
if( rtiff->memcpy )
dst = VIPS_REGION_ADDR( or, 0, r->top );
else
dst = tbuf;
strip = r->top / rtiff->rows_per_strip;
length = TIFFReadEncodedStrip( rtiff->tiff, strip, dst, (tsize_t) -1 );
if( length == -1 ) {
vips_error( "tiff2vips", "%s", _( "read error" ) );
return( -1 );
}
/* If necessary, unpack to destination.
*/
if( !rtiff->memcpy ) {
int y;
for( y = 0; y < r->height; y++ ) {
VipsPel *p = tbuf + y * rtiff->scanline_size;
VipsPel *q = VIPS_REGION_ADDR( or, 0, r->top );
rtiff->sfn( q, p,
or->im->Xsize, rtiff->client );
}
}
return( 0 );
}
/* Stripwise reading.
*
* We could potentially read strips in any order, but this would give
* catastrophic performance for opertions like 90 degress rotate on a
* large image. Only offer sequential read.
*/ */
static int static int
read_stripwise( ReadTiff *rtiff, VipsImage *out ) read_stripwise( ReadTiff *rtiff, VipsImage *out )
{ {
uint32 rows_per_strip; VipsImage **t = (VipsImage **)
tsize_t scanline_size; vips_object_local_array( VIPS_OBJECT( out ), 3 );
tsize_t strip_size;
int number_of_strips;
VipsPel *vbuf;
tdata_t tbuf; tdata_t tbuf;
tstrip_t strip;
tsize_t length;
int y;
int i;
VipsPel *p;
#ifdef DEBUG #ifdef DEBUG
printf( "tiff2vips: read_stripwise\n" ); printf( "tiff2vips: read_stripwise\n" );
#endif /*DEBUG*/ #endif /*DEBUG*/
if( parse_header( rtiff, out ) ) t[0] = vips_image_new();
if( parse_header( rtiff, t[0] ) )
return( -1 ); return( -1 );
if( !tfget32( rtiff->tiff, TIFFTAG_ROWSPERSTRIP, &rows_per_strip ) ) vips_demand_hint( t[0],
VIPS_DEMAND_STYLE_FATSTRIP, NULL );
if( !tfget32( rtiff->tiff,
TIFFTAG_ROWSPERSTRIP, &rtiff->rows_per_strip ) )
return( -1 ); return( -1 );
scanline_size = TIFFScanlineSize( rtiff->tiff ); rtiff->scanline_size = TIFFScanlineSize( rtiff->tiff );
strip_size = TIFFStripSize( rtiff->tiff ); rtiff->strip_size = TIFFStripSize( rtiff->tiff );
number_of_strips = TIFFNumberOfStrips( rtiff->tiff ); rtiff->number_of_strips = TIFFNumberOfStrips( rtiff->tiff );
#ifdef DEBUG #ifdef DEBUG
printf( "read_stripwise: rows_per_strip = %u\n", rows_per_strip ); printf( "read_stripwise: rows_per_strip = %u\n",
printf( "read_stripwise: scanline_size = %d\n", scanline_size ); rtiff->rows_per_strip );
printf( "read_stripwise: strip_size = %d\n", strip_size ); printf( "read_stripwise: scanline_size = %d\n",
printf( "read_stripwise: number_of_strips = %d\n", number_of_strips ); rtiff->scanline_size );
printf( "read_stripwise: strip_size = %d\n",
rtiff->strip_size );
printf( "read_stripwise: number_of_strips = %d\n",
rtiff->number_of_strips );
#endif /*DEBUG*/ #endif /*DEBUG*/
/* Make buffers. /* Read each strip to this. We only need one for stripwise_generate)(_
* since it's single-threaded by tilecache().
*/ */
if( !(vbuf = VIPS_ARRAY( out, if( !(tbuf = vips_malloc( VIPS_OBJECT( out ), rtiff->strip_size )) )
VIPS_IMAGE_SIZEOF_LINE( out ), VipsPel )) ||
!(tbuf = vips_malloc( VIPS_OBJECT( out ), strip_size )) )
return( -1 ); return( -1 );
for( strip = 0, y = 0; /* Each tile in the cache is the size of a strip. Cache enough tiles
strip < number_of_strips; * for two lines of SMALLTILE.
strip += 1, y += rows_per_strip ) { */
length = TIFFReadEncodedStrip( rtiff->tiff, if(
strip, tbuf, (tsize_t) -1 ); vips_image_generate( t[0],
if( length == -1 ) { NULL, tiff2vips_stripwise_generate, NULL,
vips_error( "tiff2vips", "%s", _( "read error" ) ); rtiff, tbuf ) ||
return( -1 ); vips_sequential( t[0], &t[1], NULL ) ||
} vips_tilecache( t[1], &t[2],
"tile_width", t[0]->Xsize,
for( p = tbuf, i = 0; "tile_height", rtiff->rows_per_strip,
i < rows_per_strip && y + i < out->Ysize; "max_tiles",
i += 1, p += scanline_size ) { 1 + (2 * VIPS__TILE_HEIGHT) /
/* If we need to unpack the data, go via a buffer. rtiff->rows_per_strip,
* Otherwise we can write directly from the strip. NULL ) ||
*/ vips_image_write( t[2], out ) )
if( rtiff->memcpy ) { return( -1 );
if( vips_image_write_line( out, y + i, p ) )
return( -1 );
}
else {
rtiff->sfn( vbuf, p,
out->Xsize, rtiff->client );
if( vips_image_write_line( out, y + i, vbuf ) )
return( -1 );
}
}
}
return( 0 ); return( 0 );
} }
@ -1369,7 +1422,6 @@ static void
readtiff_destroy( VipsObject *object, ReadTiff *rtiff ) readtiff_destroy( VipsObject *object, ReadTiff *rtiff )
{ {
VIPS_FREEF( TIFFClose, rtiff->tiff ); VIPS_FREEF( TIFFClose, rtiff->tiff );
VIPS_FREEF( g_mutex_free, rtiff->tlock );
} }
static ReadTiff * static ReadTiff *
@ -1388,7 +1440,6 @@ readtiff_new( const char *filename, VipsImage *out, int page )
rtiff->memcpy = FALSE; rtiff->memcpy = FALSE;
rtiff->twidth = 0; rtiff->twidth = 0;
rtiff->theight = 0; rtiff->theight = 0;
rtiff->tlock = g_mutex_new();
g_signal_connect( out, "close", g_signal_connect( out, "close",
G_CALLBACK( readtiff_destroy ), rtiff ); G_CALLBACK( readtiff_destroy ), rtiff );

View File

@ -77,6 +77,8 @@ vips_foreign_load_tiff_get_flags_filename( const char *filename )
flags = 0; flags = 0;
if( vips__istifftiled( filename ) ) if( vips__istifftiled( filename ) )
flags |= VIPS_FOREIGN_PARTIAL; flags |= VIPS_FOREIGN_PARTIAL;
else
flags |= VIPS_FOREIGN_SEQUENTIAL;
return( flags ); return( flags );
} }

View File

@ -287,7 +287,7 @@ png2vips_header( Read *read, VipsImage *out )
/* We're always supposed to set dhint. /* We're always supposed to set dhint.
*/ */
vips_demand_hint( out, vips_demand_hint( out,
VIPS_DEMAND_STYLE_THINSTRIP, NULL ); VIPS_DEMAND_STYLE_FATSTRIP, NULL );
return( 0 ); return( 0 );
} }

View File

@ -302,7 +302,7 @@ sink_memory_init( SinkMemory *memory, VipsImage *image )
} }
/** /**
* vips_sink_memory2: * vips_sink_memory:
* @im: generate this image to memory * @im: generate this image to memory
* *
* Loops over an image, generating it to a memory buffer attached to the * Loops over an image, generating it to a memory buffer attached to the
@ -313,7 +313,7 @@ sink_memory_init( SinkMemory *memory, VipsImage *image )
* Returns: 0 on success, or -1 on error. * Returns: 0 on success, or -1 on error.
*/ */
int int
vips_sink_memory2( VipsImage *image ) vips_sink_memory( VipsImage *image )
{ {
SinkMemory memory; SinkMemory memory;
int result; int result;