start adding TIFF stream load

streams now have a seek() vfunc
This commit is contained in:
John Cupitt 2019-10-16 18:28:46 +01:00
parent 430fd97397
commit fb2ab23e26
6 changed files with 316 additions and 36 deletions

View File

@ -95,6 +95,13 @@ int vips__tiff_read_header_buffer( const void *buf, size_t len, VipsImage *out,
int vips__tiff_read_buffer( const void *buf, size_t len, VipsImage *out, int vips__tiff_read_buffer( const void *buf, size_t len, VipsImage *out,
int page, int n, gboolean autorotate ); int page, int n, gboolean autorotate );
gboolean vips__istiff_stream( VipsStreamInput *input );
gboolean vips__istifftiled_stream( VipsStreamInput *input );
int vips__tiff_read_header_stream( VipsStreamInput *input, VipsImage *out,
int page, int n, gboolean autorotate );
int vips__tiff_read_stream( VipsStreamInput *input, VipsImage *out,
int page, int n, gboolean autorotate );
extern const char *vips__foreign_tiff_suffs[]; extern const char *vips__foreign_tiff_suffs[];
int vips__isanalyze( const char *filename ); int vips__isanalyze( const char *filename );

View File

@ -300,6 +300,100 @@ vips__tiff_openin_buffer( VipsImage *image, const void *data, size_t length )
return( tiff ); return( tiff );
} }
/* TIFF input from a vips stream.
*/
static tsize_t
openin_stream_read( thandle_t st, tdata_t data, tsize_t size )
{
VipsStreamInput *input = (VipsStreamInput *) st;
return( vips_stream_input_read( input, data, size ) );
}
static tsize_t
openin_stream_write( thandle_t st, tdata_t buffer, tsize_t size )
{
g_assert_not_reached();
return( 0 );
}
static toff_t
openin_stream_seek( thandle_t st, toff_t position, int whence )
{
VipsStreamInput *input = (VipsStreamInput *) st;
return( vips_stream_input_seek( input, position, whence ) );
}
static int
openin_stream_close( thandle_t st )
{
VipsStreamInput *input = (VipsStreamInput *) st;
VIPS_UNREF( input );
return( 0 );
}
static toff_t
openin_stream_size( thandle_t st )
{
/* Do we need this?
*/
printf( "aaaaargh!!\n" );
g_assert( FALSE );
return( 0 );
}
static int
openin_stream_map( thandle_t st, tdata_t *start, toff_t *len )
{
g_assert_not_reached();
return( 0 );
}
static void
openin_stream_unmap( thandle_t st, tdata_t start, toff_t len )
{
g_assert_not_reached();
return;
}
TIFF *
vips__tiff_openin_stream( VipsImage *image, VipsStreamInput *input )
{
TIFF *tiff;
#ifdef DEBUG
printf( "vips__tiff_openin_buffer:\n" );
#endif /*DEBUG*/
/* Unreffed on close(), see above.
*/
g_object_ref( input );
if( !(tiff = TIFFClientOpen( "stream input", "rm",
(thandle_t) input,
openin_stream_read,
openin_stream_write,
openin_stream_seek,
openin_stream_close,
openin_stream_size,
openin_stream_map,
openin_stream_unmap )) ) {
vips_error( "vips__tiff_openin_stream", "%s",
_( "unable to open stream for input" ) );
return( NULL );
}
return( tiff );
}
/* TIFF output to a memory buffer. /* TIFF output to a memory buffer.
*/ */

View File

@ -45,6 +45,8 @@ TIFF *vips__tiff_openin_buffer( VipsImage *image,
TIFF *vips__tiff_openout_buffer( VipsImage *image, TIFF *vips__tiff_openout_buffer( VipsImage *image,
gboolean bigtiff, void **out_data, size_t *out_length ); gboolean bigtiff, void **out_data, size_t *out_length );
TIFF *vips__tiff_openin_stream( VipsImage *image, VipsStreamInput *input );
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif /*__cplusplus*/ #endif /*__cplusplus*/

View File

@ -2591,4 +2591,104 @@ vips__istifftiled_buffer( const void *buf, size_t len )
return( tiled ); return( tiled );
} }
static gboolean
vips__testtiff_stream( VipsStreamInput *input, TiffPropertyFn fn )
{
VipsImage *im;
TIFF *tif;
gboolean property;
vips__tiff_init();
im = vips_image_new();
if( vips_stream_input_rewind( input ) )
return( FALSE );
if( !(tif = vips__tiff_openin_stream( im, input )) ) {
g_object_unref( im );
vips_error_clear();
return( FALSE );
}
property = fn ? fn( tif ) : TRUE;
TIFFClose( tif );
g_object_unref( im );
return( property );
}
gboolean
vips__istiff_stream( VipsStreamInput *input )
{
return( vips__testtiff_stream( input, NULL ) );
}
gboolean
vips__istifftiled_stream( VipsStreamInput *input )
{
return( vips__testtiff_stream( input, TIFFIsTiled ) );
}
static Rtiff *
rtiff_new_stream( VipsStreamInput *input, VipsImage *out,
int page, int n, gboolean autorotate )
{
Rtiff *rtiff;
if( !(rtiff = rtiff_new( out, page, n, autorotate )) ||
!(rtiff->tiff = vips__tiff_openin_stream( out, input )) ||
rtiff_header_read_all( rtiff ) )
return( NULL );
return( rtiff );
}
int
vips__tiff_read_header_stream( VipsStreamInput *input, VipsImage *out,
int page, int n, gboolean autorotate )
{
Rtiff *rtiff;
vips__tiff_init();
if( !(rtiff = rtiff_new_stream( input, out, page, n, autorotate )) )
return( -1 );
if( rtiff_set_header( rtiff, out ) )
return( -1 );
vips__tiff_read_header_orientation( rtiff, out );
return( 0 );
}
int
vips__tiff_read_stream( VipsStreamInput *input, VipsImage *out,
int page, int n, gboolean autorotate )
{
Rtiff *rtiff;
#ifdef DEBUG
printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
printf( "tiff2vips: libtiff starting for buffer %p\n", buf );
#endif /*DEBUG*/
vips__tiff_init();
if( !(rtiff = rtiff_new_stream( input, out, page, n, autorotate )) )
return( -1 );
if( rtiff->header.tiled ) {
if( rtiff_read_tilewise( rtiff, out ) )
return( -1 );
}
else {
if( rtiff_read_stripwise( rtiff, out ) )
return( -1 );
}
return( 0 );
}
#endif /*HAVE_TIFF*/ #endif /*HAVE_TIFF*/

View File

@ -106,7 +106,7 @@ const char *vips_stream_filename( VipsStream *stream );
/* Read from something like a socket, file or memory area and present the data /* Read from something like a socket, file or memory area and present the data
* with a simple seek / read interface. * with a simple seek / read interface.
* *
* During the header phase, we save data from unseekable sources in a buffer * During the header phase, we save data from unseekable streams in a buffer
* so readers can rewind and read again. We don't buffer data during the * so readers can rewind and read again. We don't buffer data during the
* decode stage. * decode stage.
*/ */
@ -116,7 +116,7 @@ typedef struct _VipsStreamInput {
/* We have two phases: /* We have two phases:
* *
* During the header phase, we save bytes read from the input (if this * During the header phase, we save bytes read from the input (if this
* is an unseekable source) so that we can rewind and try again, if * is an unseekable stream) so that we can rewind and try again, if
* necessary. * necessary.
* *
* Once we reach decode phase, we no longer support rewind and the * Once we reach decode phase, we no longer support rewind and the
@ -124,13 +124,13 @@ typedef struct _VipsStreamInput {
*/ */
gboolean decode; gboolean decode;
/* TRUE is this descriptor supports lseek(). If not, then we save data /* TRUE if this descriptor supports lseek(). If not, then we save data
* read during header phase in a buffer. * read during the header phase in a buffer.
*/ */
gboolean seekable; gboolean seekable;
/* TRUE is this descriptor supports mmap(). If not, then we have to /* TRUE if this descriptor supports mmap(). If not, then we have to
* read() the whole thing. * read() the whole stream if the loader needs the entire image.
*/ */
gboolean mapable; gboolean mapable;
@ -149,11 +149,11 @@ typedef struct _VipsStreamInput {
*/ */
GByteArray *sniff; GByteArray *sniff;
/* For a memory source, the blob we read from. /* For a memory stream, the blob we read from.
*/ */
VipsBlob *blob; VipsBlob *blob;
/* If we've mmaped the file, the base and length. /* If we've mmaped the file, the base and length of the mapped area.
*/ */
const void *baseaddr; const void *baseaddr;
size_t length; size_t length;
@ -165,9 +165,27 @@ typedef struct _VipsStreamInputClass {
/* Subclasses can define these to implement other input methods. /* Subclasses can define these to implement other input methods.
*/ */
/* Read up to N bytes from the stream into the supplied buffer,
* returning the number of bytes actually read.
*
* -1 on error, 0 on EOF.
*/
ssize_t (*read)( VipsStreamInput *, unsigned char *, size_t ); ssize_t (*read)( VipsStreamInput *, unsigned char *, size_t );
const void * (*map)( VipsStreamInput *, size_t * );
int (*rewind)( VipsStreamInput * ); /* Map the entire stream into memory, for example with mmap(). Return
* the base and size of the mapped area.
*
* If this is not defined, the file will be read in with repeated
* calls to ->read().
*
* NULL on error.
*/
const void *(*map)( VipsStreamInput *, size_t * );
/* Seek to a certain position, args exactly as lseek(2).
*/
off_t (*seek)( VipsStreamInput *, off_t offset, int );
/* Shut down anything that can safely restarted. For example, if /* Shut down anything that can safely restarted. For example, if
* there's a fd that supports lseek(), it can be closed, since later * there's a fd that supports lseek(), it can be closed, since later
@ -192,6 +210,8 @@ VipsStreamInput *vips_stream_input_new_from_options( const char *options );
ssize_t vips_stream_input_read( VipsStreamInput *input, ssize_t vips_stream_input_read( VipsStreamInput *input,
unsigned char *data, size_t length ); unsigned char *data, size_t length );
const void *vips_stream_input_map( VipsStreamInput *input, size_t *length ); const void *vips_stream_input_map( VipsStreamInput *input, size_t *length );
off_t vips_stream_input_seek( VipsStreamInput *input,
off_t offset, int whence );
int vips_stream_input_rewind( VipsStreamInput *input ); int vips_stream_input_rewind( VipsStreamInput *input );
void vips_stream_input_minimise( VipsStreamInput *input ); void vips_stream_input_minimise( VipsStreamInput *input );
void vips_stream_input_decode( VipsStreamInput *input ); void vips_stream_input_decode( VipsStreamInput *input );

View File

@ -345,29 +345,29 @@ vips_stream_input_map_real( VipsStreamInput *input, size_t *length )
return( file_baseaddr ); return( file_baseaddr );
} }
static int static off_t
vips_stream_input_rewind_real( VipsStreamInput *input ) vips_stream_input_seek_real( VipsStreamInput *input, off_t offset, int whence )
{ {
VipsStream *stream = VIPS_STREAM( input ); VipsStream *stream = VIPS_STREAM( input );
VIPS_DEBUG_MSG( "vips_stream_input_rewind_real:\n" );
if( input->seekable &&
stream->descriptor != -1 ) {
off_t new_pos; off_t new_pos;
VIPS_DEBUG_MSG( " rewinding desriptor %d\n", VIPS_DEBUG_MSG( "vips_stream_input_seek_real:\n" );
stream->descriptor );
new_pos = lseek( stream->descriptor, 0, SEEK_SET ); if( !input->seekable ||
stream->descriptor == -1 ) {
vips_error( STREAM_NAME( stream ), "%s", _( "not seekable" ) );
return( -1 );
}
new_pos = lseek( stream->descriptor, offset, whence );
if( new_pos == -1 ) { if( new_pos == -1 ) {
vips_error_system( errno, STREAM_NAME( stream ), vips_error_system( errno, STREAM_NAME( stream ),
"%s", _( "unable to rewind" ) ); "%s", _( "seek error" ) );
return( 0 ); return( -1 );
}
} }
return( 0 ); return( new_pos );
} }
static void static void
@ -398,7 +398,7 @@ vips_stream_input_class_init( VipsStreamInputClass *class )
class->read = vips_stream_input_read_real; class->read = vips_stream_input_read_real;
class->map = vips_stream_input_map_real; class->map = vips_stream_input_map_real;
class->rewind = vips_stream_input_rewind_real; class->seek = vips_stream_input_seek_real;
class->minimise = vips_stream_input_minimise_real; class->minimise = vips_stream_input_minimise_real;
VIPS_ARG_BOXED( class, "blob", 3, VIPS_ARG_BOXED( class, "blob", 3,
@ -671,22 +671,79 @@ vips_stream_input_map( VipsStreamInput *input, size_t *length )
return( input->header_bytes->data ); return( input->header_bytes->data );
} }
int off_t
vips_stream_input_rewind( VipsStreamInput *input ) vips_stream_input_seek( VipsStreamInput *input, off_t offset, int whence )
{ {
VipsStreamInputClass *class = VIPS_STREAM_INPUT_GET_CLASS( input ); VipsStreamInputClass *class = VIPS_STREAM_INPUT_GET_CLASS( input );
VIPS_DEBUG_MSG( "vips_stream_input_rewind:\n" ); off_t new_pos;
VIPS_DEBUG_MSG( "vips_stream_input_seek:\n" );
switch( whence ) {
case SEEK_SET:
new_pos = offset;
break;
case SEEK_CUR:
new_pos = input->read_position + offset;
break;
case SEEK_END:
/* TODO .. I don't think any loader needs SEEK_END.
*/
default:
vips_error( STREAM_NAME( input ), "%s", _( "bad 'whence'" ) );
return( -1 );
break;
}
if( !input->seekable ) {
/* We can seek on non-seekable streams during the header phase.
*/
if( input->decode ) { if( input->decode ) {
vips_error( STREAM_NAME( input ), vips_error( STREAM_NAME( input ),
"%s", _( "can't rewind after decode begins" ) ); "%s", _( "can't rewind after decode begins" ) );
return( -1 ); return( -1 );
} }
input->read_position = 0; g_assert( input->header_bytes );
return( class->rewind( input ) ); /* We may not have read up to the new position.
*/
while( input->read_position < new_pos ) {
unsigned char buffer[4096];
if( vips_stream_input_read( input, buffer, 4096 ) )
return( -1 );
}
}
else if( input->blob ||
input->baseaddr ) {
/* Memory streams and mapped streams don't need a seek.
*/
;
}
else {
new_pos = class->seek( input, offset, whence );
if( new_pos == -1 )
return( -1 );
}
input->read_position = new_pos;
return( new_pos );
}
int
vips_stream_input_rewind( VipsStreamInput *input )
{
VIPS_DEBUG_MSG( "vips_stream_input_rewind:\n" );
if( vips_stream_input_seek( input, 0, SEEK_SET ) != 0 )
return( -1 );
return( 0 );
} }
void void