faster tiled tiff read, faster im_tile_cache()
This commit is contained in:
parent
7966a36446
commit
f70ae48de3
@ -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();
|
||||
|
@ -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 );
|
||||
|
@ -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.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user