start adding TIFF stream load
streams now have a seek() vfunc
This commit is contained in:
parent
430fd97397
commit
fb2ab23e26
@ -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 );
|
||||||
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -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*/
|
||||||
|
@ -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*/
|
||||||
|
@ -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 );
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user