faster tiled tiff read, faster im_tile_cache()

This commit is contained in:
John Cupitt 2010-12-12 17:46:14 +00:00
parent 7966a36446
commit f70ae48de3
3 changed files with 118 additions and 111 deletions

View File

@ -113,6 +113,8 @@
* subsampling schemes (esp. subsampled YCbCr), and it's a bit quicker
* 4/2/10
* - gtkdoc
* 12/12/10
* - oops, we can just memcpy() now heh
*/
/*
@ -167,7 +169,6 @@ im_tiff2vips( const char *tiffile, IMAGE *im )
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vips/vips.h>
#include <vips/internal.h>
@ -181,7 +182,7 @@ im_tiff2vips( const char *tiffile, IMAGE *im )
/* Scanline-type process function.
*/
typedef void (*scanline_process_fn)( PEL *q, PEL *p, int n, void *user );
typedef void (*scanline_process_fn)( PEL *q, PEL *p, int n, void *client );
/* Stuff we track during a read.
*/
@ -204,6 +205,10 @@ typedef struct {
scanline_process_fn sfn;
void *client;
/* Set this is the processfn is just doing a memcpy.
*/
gboolean memcpy;
/* Geometry.
*/
int twidth, theight; /* Tile size */
@ -356,7 +361,6 @@ parse_labpack( ReadTiff *rtiff, IMAGE *out )
out->Type = IM_TYPE_LAB;
rtiff->sfn = labpack_line;
rtiff->client = NULL;
return( 0 );
}
@ -395,7 +399,6 @@ parse_labs( ReadTiff *rtiff, IMAGE *out )
out->Type = IM_TYPE_LABS;
rtiff->sfn = labs_line;
rtiff->client = NULL;
return( 0 );
}
@ -554,17 +557,14 @@ parse_greyscale16( ReadTiff *rtiff, int pm, IMAGE *out )
return( 0 );
}
/* Per-scanline process function for 32-bit floating point greyscale images.
/* Per-scanline process function when we just need to copy.
*/
static void
greyscale32f_line( PEL *q, PEL *p, int n )
memcpy_line( PEL *q, PEL *p, int n, void *client )
{
float *p1 = (float *) p;
float *q1 = (float *) q;
int x;
IMAGE *im = (IMAGE *) client;
for( x = 0; x < n; x++ )
q1[x] = p1[x];
memcpy( q, p, n * IM_IMAGE_SIZEOF_PEL( im ) );
}
/* Read a 32-bit floating point greyscale TIFF image. What do we do about
@ -582,8 +582,9 @@ parse_greyscale32f( ReadTiff *rtiff, int pm, IMAGE *out )
out->Coding = IM_CODING_NONE;
out->Type = IM_TYPE_B_W;
rtiff->sfn = (scanline_process_fn) greyscale32f_line;
rtiff->client = NULL;
rtiff->sfn = memcpy_line;
rtiff->client = out;
rtiff->memcpy = TRUE;
return( 0 );
}
@ -662,22 +663,6 @@ parse_palette( ReadTiff *rtiff, IMAGE *out )
return( 0 );
}
/* Per-scanline process function for 8-bit RGB/RGBA/CMYK/CMYKA/etc.
*/
static void
rgbcmyk8_line( PEL *q, PEL *p, int n, IMAGE *im )
{
int x, b;
for( x = 0; x < n; x++ ) {
for( b = 0; b < im->Bands; b++ )
q[b] = p[b];
q += im->Bands;
p += im->Bands;
}
}
/* Read an 8-bit RGB/RGBA image.
*/
static int
@ -702,30 +687,13 @@ parse_rgb8( ReadTiff *rtiff, IMAGE *out )
out->Coding = IM_CODING_NONE;
out->Type = IM_TYPE_sRGB;
rtiff->sfn = (scanline_process_fn) rgbcmyk8_line;
rtiff->sfn = memcpy_line;
rtiff->client = out;
rtiff->memcpy = TRUE;
return( 0 );
}
/* Per-scanline process function for RGB/RGBA 16.
*/
static void
rgb16_line( PEL *q, PEL *p, int n, IMAGE *im )
{
int x, b;
unsigned short *p1 = (unsigned short *) p;
unsigned short *q1 = (unsigned short *) q;
for( x = 0; x < n; x++ ) {
for( b = 0; b < im->Bands; b++ )
q1[b] = p1[b];
q1 += im->Bands;
p1 += im->Bands;
}
}
/* Read a 16-bit RGB/RGBA image.
*/
static int
@ -750,31 +718,13 @@ parse_rgb16( ReadTiff *rtiff, IMAGE *out )
out->Coding = IM_CODING_NONE;
out->Type = IM_TYPE_RGB16;
rtiff->sfn = (scanline_process_fn) rgb16_line;
rtiff->sfn = memcpy_line;
rtiff->client = out;
rtiff->memcpy = TRUE;
return( 0 );
}
/* Per-scanline process function for 32f.
*/
static void
r32f_line( PEL *q, PEL *p, int n, void *dummy )
{
int x;
float *p1 = (float *) p;
float *q1 = (float *) q;
for( x = 0; x < n; x++ ) {
q1[0] = p1[0];
q1[1] = p1[1];
q1[2] = p1[2];
q1 += 3;
p1 += 3;
}
}
/* Read a 32-bit float image. RGB or LAB, with or without alpha.
*/
static int
@ -788,7 +738,7 @@ parse_32f( ReadTiff *rtiff, int pm, IMAGE *out )
/* Can be 4 for images with an alpha channel.
*/
assert( bands == 3 || bands == 4 );
g_assert( bands == 3 || bands == 4 );
out->Bands = bands;
out->BandFmt = IM_BANDFMT_FLOAT;
@ -804,11 +754,12 @@ parse_32f( ReadTiff *rtiff, int pm, IMAGE *out )
break;
default:
assert( 0 );
g_assert( 0 );
}
rtiff->sfn = r32f_line;
rtiff->client = NULL;
rtiff->sfn = memcpy_line;
rtiff->client = out;
rtiff->memcpy = TRUE;
return( 0 );
}
@ -838,8 +789,9 @@ parse_cmyk( ReadTiff *rtiff, IMAGE *out )
out->Coding = IM_CODING_NONE;
out->Type = IM_TYPE_CMYK;
rtiff->sfn = (scanline_process_fn) rgbcmyk8_line;
rtiff->sfn = memcpy_line;
rtiff->client = out;
rtiff->memcpy = TRUE;
return( 0 );
}
@ -1108,7 +1060,7 @@ parse_header( ReadTiff *rtiff, IMAGE *out )
* to vips in parallel.
*/
static void *
seq_start( IMAGE *out, void *a, void *b )
tiff_seq_start( IMAGE *out, void *a, void *b )
{
ReadTiff *rtiff = (ReadTiff *) a;
tdata_t *buf;
@ -1119,10 +1071,41 @@ seq_start( IMAGE *out, void *a, void *b )
return( (void *) buf );
}
/* Paint a tile from the file. This is a
* special-case for a region is exactly a tiff tile, and pixels need no
* conversion. In this case, libtiff can read tiles directly to our output
* region.
*/
static int
tiff_fill_region_aligned( REGION *out, void *seq, void *a, void *b )
{
ReadTiff *rtiff = (ReadTiff *) a;
Rect *r = &out->valid;
g_assert( (r->left % rtiff->twidth) == 0 );
g_assert( (r->top % rtiff->theight) == 0 );
g_assert( r->width == rtiff->twidth );
g_assert( r->height == rtiff->theight );
g_assert( IM_REGION_LSKIP( out ) == IM_REGION_SIZEOF_LINE( out ) );
/* Read that tile directly into the vips tile.
*/
g_mutex_lock( rtiff->tlock );
if( TIFFReadTile( rtiff->tiff,
IM_REGION_ADDR( out, r->left, r->top ),
r->left, r->top, 0, 0 ) < 0 ) {
g_mutex_unlock( rtiff->tlock );
return( -1 );
}
g_mutex_unlock( rtiff->tlock );
return( 0 );
}
/* Loop over the output region, painting in tiles from the file.
*/
static int
fill_region( REGION *out, void *seq, void *a, void *b )
tiff_fill_region( REGION *out, void *seq, void *a, void *b )
{
tdata_t *buf = (tdata_t *) seq;
ReadTiff *rtiff = (ReadTiff *) a;
@ -1147,6 +1130,17 @@ fill_region( REGION *out, void *seq, void *a, void *b )
int x, y, z;
/* Special case: we are filling a single tile exactly sizeed to match
* the tiff tile, and we have no repacking to do for this format.
*/
if( rtiff->memcpy &&
r->left % rtiff->twidth == 0 &&
r->top % rtiff->theight == 0 &&
r->width == rtiff->twidth &&
r->height == rtiff->theight &&
IM_REGION_LSKIP( out ) == IM_REGION_SIZEOF_LINE( out ) )
return( tiff_fill_region_aligned( out, seq, a, b ) );
for( y = ys; y < IM_RECT_BOTTOM( r ); y += rtiff->theight )
for( x = xs; x < IM_RECT_RIGHT( r ); x += rtiff->twidth ) {
Rect tile;
@ -1191,7 +1185,7 @@ fill_region( REGION *out, void *seq, void *a, void *b )
}
static int
seq_stop( void *seq, void *a, void *b )
tiff_seq_stop( void *seq, void *a, void *b )
{
im_free( seq );
@ -1233,7 +1227,8 @@ read_tilewise( ReadTiff *rtiff, IMAGE *out )
*/
if( im_demand_hint( raw, IM_SMALLTILE, NULL ) ||
im_generate( raw,
seq_start, fill_region, seq_stop, rtiff, NULL ) )
tiff_seq_start, tiff_fill_region, tiff_seq_stop,
rtiff, NULL ) )
return( -1 );
/* Copy to out, adding a cache. Enough tiles for two complete rows.
@ -1348,6 +1343,7 @@ readtiff_new( const char *filename, IMAGE *out )
rtiff->tiff = NULL;
rtiff->sfn = NULL;
rtiff->client = NULL;
rtiff->memcpy = FALSE;
rtiff->twidth = 0;
rtiff->theight = 0;
rtiff->tlock = g_mutex_new();

View File

@ -11,6 +11,8 @@
* threads
* 4/2/10
* - gtkdoc
* 12/12/10
* - use im_prepare_to() and avoid making a sequence for every cache tile
*/
/*
@ -51,7 +53,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <vips/vips.h>
#include <vips/thread.h>
@ -103,7 +104,7 @@ tile_destroy( Tile *tile )
read->cache = g_slist_remove( read->cache, tile );
read->ntiles -= 1;
assert( read->ntiles >= 0 );
g_assert( read->ntiles >= 0 );
tile->read = NULL;
IM_FREEF( im_region_free, tile->region );
@ -166,17 +167,37 @@ tile_new( Read *read )
tile->x = -1;
tile->y = -1;
read->cache = g_slist_prepend( read->cache, tile );
assert( read->ntiles >= 0 );
g_assert( read->ntiles >= 0 );
read->ntiles += 1;
if( !(tile->region = im_region_create( read->in )) ) {
tile_destroy( tile );
return( NULL );
}
im__region_no_ownership( tile->region );
return( tile );
}
static int
tile_move( Tile *tile, int x, int y )
{
Rect area;
tile->x = x;
tile->y = y;
area.left = x;
area.top = y;
area.width = tile->read->tile_width;
area.height = tile->read->tile_height;
if( im_region_buffer( tile->region, &area ) )
return( -1 );
return( 0 );
}
/* Do we have a tile in the cache?
*/
static Tile *
@ -197,7 +218,7 @@ tile_search( Read *read, int x, int y )
static void
tile_touch( Tile *tile )
{
assert( tile->read->ntiles >= 0 );
g_assert( tile->read->ntiles >= 0 );
tile->time = tile->read->time++;
}
@ -205,28 +226,21 @@ tile_touch( Tile *tile )
/* Fill a tile with pixels.
*/
static int
tile_fill( Tile *tile, int x, int y )
tile_fill( Tile *tile, REGION *in )
{
Rect area;
tile->x = x;
tile->y = y;
#ifdef DEBUG
printf( "im_tile_cache: filling tile %d x %d\n", tile->x, tile->y );
#endif /*DEBUG*/
area.left = x;
area.top = y;
area.left = tile->x;
area.top = tile->y;
area.width = tile->read->tile_width;
area.height = tile->read->tile_height;
if( im_prepare( tile->region, &area ) )
return( -1 );
/* Make sure these pixels aren't part of this thread's buffer cache
* ... they may be read out by another thread.
*/
im__region_no_ownership( tile->region );
if( im_prepare_to( in, tile->region, &area, area.left, area.top ) )
return( -1 );
tile_touch( tile );
@ -237,7 +251,7 @@ tile_fill( Tile *tile, int x, int y )
* reuse LRU.
*/
static Tile *
tile_find( Read *read, int x, int y )
tile_find( Read *read, REGION *in, int x, int y )
{
Tile *tile;
int oldest;
@ -256,7 +270,8 @@ tile_find( Read *read, int x, int y )
if( read->max_tiles == -1 ||
read->ntiles < read->max_tiles ) {
if( !(tile = tile_new( read )) ||
tile_fill( tile, x, y ) )
tile_move( tile, x, y ) ||
tile_fill( tile, in ) )
return( NULL );
return( tile );
@ -275,20 +290,14 @@ tile_find( Read *read, int x, int y )
}
}
assert( tile );
/* The tile may have been created by another thread if we are sharing
* the tile cache between several readers. Take ownership of the tile
* to stop assert() failures in im_prepare(). This is safe, since we
* are in a mutex.
*/
im__region_take_ownership( tile->region );
g_assert( tile );
#ifdef DEBUG
printf( "im_tile_cache: reusing tile %d x %d\n", tile->x, tile->y );
#endif /*DEBUG*/
if( tile_fill( tile, x, y ) )
if( tile_move( tile, x, y ) ||
tile_fill( tile, in ) )
return( NULL );
return( tile );
@ -303,8 +312,8 @@ copy_region( REGION *from, REGION *to, Rect *area )
/* Area should be inside both from and to.
*/
assert( im_rect_includesrect( &from->valid, area ) );
assert( im_rect_includesrect( &to->valid, area ) );
g_assert( im_rect_includesrect( &from->valid, area ) );
g_assert( im_rect_includesrect( &to->valid, area ) );
/* Loop down common area, copying.
*/
@ -319,9 +328,10 @@ copy_region( REGION *from, REGION *to, Rect *area )
/* Loop over the output region, filling with data from cache.
*/
static int
fill_region( REGION *out, void *seq, void *a, void *b )
tile_cache_fill( REGION *out, void *seq, void *a, void *b )
{
Read *read = (Read *) a;
REGION *in = (REGION *) seq;
Read *read = (Read *) b;
const int tw = read->tile_width;
const int th = read->tile_height;
Rect *r = &out->valid;
@ -341,7 +351,7 @@ fill_region( REGION *out, void *seq, void *a, void *b )
Rect tarea;
Rect hit;
if( !(tile = tile_find( read, x, y )) ) {
if( !(tile = tile_find( read, in, x, y )) ) {
g_mutex_unlock( read->lock );
return( -1 );
}
@ -405,7 +415,7 @@ im_tile_cache( IMAGE *in, IMAGE *out,
!(read = read_new( in, out,
tile_width, tile_height, max_tiles )) ||
im_generate( out,
NULL, fill_region, NULL, read, NULL ) )
im_start_one, tile_cache_fill, im_stop_one, in, read ) )
return( -1 );
return( 0 );

View File

@ -1066,7 +1066,8 @@ mask_fill( REGION *out, void *seq, void *a, void *b )
* im_prepare() on @out will always block until the pixels have been
* calculated.
*
* See also: im_cache(), im_prepare(), vips_sink_disc(), vips_sink().
* See also: im_cache(), im_tile_cache(), im_prepare(), vips_sink_disc(),
* vips_sink().
*
* Returns: 0 on sucess, -1 on error.
*/