jpeg2vips now keeps a load struct about

so we are ready to add lazy read
This commit is contained in:
John Cupitt 2012-02-19 10:54:41 +00:00
parent fa03bfb4cd
commit bbadb8d681
2 changed files with 176 additions and 86 deletions

8
TODO
View File

@ -3,6 +3,14 @@
tiff, jpg are the obvious ones
$ grep -l write_line *.c
csv.c
jpeg2vips.c
matlab.c
openexr2vips.c
ppm.c
radiance.c
- is the tif reader deadlocking sometimes?

View File

@ -43,6 +43,8 @@
* - turn into a set of read fns ready to be called from a class
* 9/1/12
* - read jfif resolution as well as exif
* 19/2/12
* - keep a read struct about, ready to go lazy read
*/
/*
@ -118,6 +120,106 @@
#include "jpeg.h"
/* Stuff we track during a read.
*/
typedef struct _ReadJpeg {
VipsImage *out;
/* Shrink by this much during load. 1, 2, 4, 8.
*/
int shrink;
/* Fail on warnings.
*/
gboolean fail;
/* Used for file input only.
*/
FILE *fp;
char *filename;
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
gboolean invert_pels;
} ReadJpeg;
static int
readjpeg_free( ReadJpeg *jpeg )
{
int result;
result = 0;
if( jpeg->eman.pub.num_warnings != 0 ) {
if( jpeg->fail ) {
vips_error( "VipsJpeg", "%s", vips_error_buffer() );
result = -1;
}
else {
vips_warn( "VipsJpeg",
_( "read gave %ld warnings" ),
jpeg->eman.pub.num_warnings );
vips_warn( "VipsJpeg", "%s", vips_error_buffer() );
}
/* Make the message only appear once.
*/
jpeg->eman.pub.num_warnings = 0;
}
VIPS_FREEF( fclose, jpeg->fp );
VIPS_FREE( jpeg->filename );
jpeg->eman.fp = NULL;
jpeg_destroy_decompress( &jpeg->cinfo );
return( result );
}
static void
readjpeg_close( VipsObject *object, ReadJpeg *jpeg )
{
(void) readjpeg_free( jpeg );
}
static ReadJpeg *
readjpeg_new( VipsImage *out, int shrink, gboolean fail )
{
ReadJpeg *jpeg;
if( !(jpeg = VIPS_NEW( out, ReadJpeg )) )
return( NULL );
jpeg->out = out;
jpeg->shrink = shrink;
jpeg->fail = fail;
jpeg->fp = NULL;
jpeg->filename = NULL;
jpeg->cinfo.err = jpeg_std_error( &jpeg->eman.pub );
jpeg->eman.pub.error_exit = vips__new_error_exit;
jpeg->eman.pub.output_message = vips__new_output_message;
jpeg->eman.fp = NULL;
jpeg_create_decompress( &jpeg->cinfo );
g_signal_connect( out, "close",
G_CALLBACK( readjpeg_close ), jpeg );
return( jpeg );
}
/* Set input to a file.
*/
static int
readjpeg_file( ReadJpeg *jpeg, const char *filename )
{
jpeg->filename = g_strdup( filename );
if( !(jpeg->fp = vips__file_open_read( filename, NULL, FALSE )) )
return( -1 );
jpeg->eman.fp = jpeg->fp;
jpeg_stdio_src( &jpeg->cinfo, jpeg->fp );
return( 0 );
}
#ifdef HAVE_EXIF
#ifdef DEBUG_VERBOSE
/* Print exif for debugging ... hacked from exif-0.6.9/actions.c
@ -362,7 +464,7 @@ get_entry_short( ExifData *ed, ExifTag tag, int *out )
}
static void
set_vips_resolution( IMAGE *im, ExifData *ed )
set_vips_resolution( VipsImage *im, ExifData *ed )
{
double xres, yres;
int unit;
@ -406,7 +508,7 @@ set_vips_resolution( IMAGE *im, ExifData *ed )
}
static int
attach_thumbnail( IMAGE *im, ExifData *ed )
attach_thumbnail( VipsImage *im, ExifData *ed )
{
if( ed->size > 0 ) {
char *thumb_copy;
@ -423,7 +525,7 @@ attach_thumbnail( IMAGE *im, ExifData *ed )
#endif /*HAVE_EXIF*/
static int
read_exif( IMAGE *im, void *data, int data_length )
read_exif( VipsImage *im, void *data, int data_length )
{
char *data_copy;
@ -494,7 +596,7 @@ read_exif( IMAGE *im, void *data, int data_length )
}
static int
read_xmp( IMAGE *im, void *data, size_t data_length )
read_xmp( VipsImage *im, void *data, size_t data_length )
{
char *data_copy;
@ -532,9 +634,10 @@ read_xmp( IMAGE *im, void *data, size_t data_length )
* do 255-pel.
*/
static int
read_jpeg_header( struct jpeg_decompress_struct *cinfo,
IMAGE *out, gboolean *invert_pels, int shrink )
read_jpeg_header( ReadJpeg *jpeg )
{
struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
jpeg_saved_marker_ptr p;
VipsInterpretation interpretation;
double xres, yres;
@ -550,11 +653,11 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
* for YUV YCCK etc.
*/
jpeg_read_header( cinfo, TRUE );
cinfo->scale_denom = shrink;
cinfo->scale_denom = jpeg->shrink;
cinfo->scale_num = 1;
jpeg_calc_output_dimensions( cinfo );
*invert_pels = FALSE;
jpeg->invert_pels = FALSE;
switch( cinfo->out_color_space ) {
case JCS_GRAYSCALE:
interpretation = VIPS_INTERPRETATION_B_W;
@ -566,7 +669,7 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
* way to spot photoshop CMYK JPGs.
*/
if( cinfo->saw_Adobe_marker )
*invert_pels = TRUE;
jpeg->invert_pels = TRUE;
break;
case JCS_RGB:
@ -611,7 +714,7 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
/* Set VIPS header.
*/
vips_image_init_fields( out,
vips_image_init_fields( jpeg->out,
cinfo->output_width, cinfo->output_height,
cinfo->output_components,
VIPS_FORMAT_UCHAR, VIPS_CODING_NONE,
@ -620,12 +723,12 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
/* Best for us, probably.
*/
vips_demand_hint( out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
vips_demand_hint( jpeg->out, VIPS_DEMAND_STYLE_FATSTRIP, NULL );
/* Interlaced jpegs need lots of memory to read, so our caller needs
* to know.
*/
(void) vips_image_set_int( out, "jpeg-multiscan",
(void) vips_image_set_int( jpeg->out, "jpeg-multiscan",
jpeg_has_multiple_scans( cinfo ) );
/* Look for EXIF and ICC profile.
@ -651,12 +754,14 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
*/
if( p->data_length > 4 &&
vips_isprefix( "Exif", (char *) p->data ) &&
read_exif( out, p->data, p->data_length ) )
read_exif( jpeg->out,
p->data, p->data_length ) )
return( -1 );
if( p->data_length > 4 &&
vips_isprefix( "http", (char *) p->data ) &&
read_xmp( out, p->data, p->data_length ) )
read_xmp( jpeg->out,
p->data, p->data_length ) )
return( -1 );
break;
@ -709,7 +814,7 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
p += app2_data_length[i];
}
vips_image_set_blob( out, VIPS_META_ICC_NAME,
vips_image_set_blob( jpeg->out, VIPS_META_ICC_NAME,
(VipsCallbackFn) vips_free, data, data_length );
}
@ -719,9 +824,10 @@ read_jpeg_header( struct jpeg_decompress_struct *cinfo,
/* Read a cinfo to a VIPS image.
*/
static int
read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out,
gboolean invert_pels )
read_jpeg_image( ReadJpeg *jpeg )
{
struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
int x, y, sz;
JSAMPROW row_pointer[1];
@ -731,28 +837,32 @@ read_jpeg_image( struct jpeg_decompress_struct *cinfo, IMAGE *out,
row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large)
( (j_common_ptr) cinfo, JPOOL_IMAGE, sz );
/* Start up decompressor.
*/
jpeg_start_decompress( cinfo );
/* Here for longjmp() from vips__new_error_exit().
*/
if( setjmp( jpeg->eman.jmp ) ) {
jpeg_finish_decompress( cinfo );
return( -1 );
}
/* Process image.
*/
for( y = 0; y < out->Ysize; y++ ) {
for( y = 0; y < jpeg->out->Ysize; y++ ) {
/* We set an error handler that longjmps() out, so I don't
* think this can fail.
*/
jpeg_read_scanlines( cinfo, &row_pointer[0], 1 );
if( invert_pels )
if( jpeg->invert_pels )
for( x = 0; x < sz; x++ )
row_pointer[0][x] = 255 - row_pointer[0][x];
if( vips_image_write_line( out, y, row_pointer[0] ) )
if( vips_image_write_line( jpeg->out, y, row_pointer[0] ) )
return( -1 );
}
/* Stop decompressor.
*/
jpeg_finish_decompress( cinfo );
return( 0 );
@ -764,63 +874,42 @@ int
vips__jpeg_read_file( const char *filename, VipsImage *out,
gboolean header_only, int shrink, gboolean fail )
{
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
FILE *fp;
ReadJpeg *jpeg;
int result;
gboolean invert_pels;
/* Make jpeg dcompression object.
*/
cinfo.err = jpeg_std_error( &eman.pub );
eman.pub.error_exit = vips__new_error_exit;
eman.pub.output_message = vips__new_output_message;
eman.fp = NULL;
if( setjmp( eman.jmp ) ) {
/* Here for longjmp() from new_error_exit().
*/
jpeg_destroy_decompress( &cinfo );
if( !(jpeg = readjpeg_new( out, shrink, fail )) )
return( -1 );
/* Here for longjmp() from vips__new_error_exit() during startup.
*/
if( setjmp( jpeg->eman.jmp ) ) {
(void) readjpeg_free( jpeg );
return( -1 );
}
jpeg_create_decompress( &cinfo );
/* Make input.
/* Set input to file.
*/
if( !(fp = vips__file_open_read( filename, NULL, FALSE )) )
return( -1 );
eman.fp = fp;
jpeg_stdio_src( &cinfo, fp );
if( readjpeg_file( jpeg, filename ) ) {
(void) readjpeg_free( jpeg );
return( -1 );
}
/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
*/
jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );
jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 1, 0xffff );
jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 2, 0xffff );
/* Convert!
*/
result = read_jpeg_header( &cinfo, out, &invert_pels, shrink );
result = read_jpeg_header( jpeg );
if( !header_only && !result )
result = read_jpeg_image( &cinfo, out, invert_pels );
result = read_jpeg_image( jpeg );
/* Close and tidy.
*/
fclose( fp );
eman.fp = NULL;
jpeg_destroy_decompress( &cinfo );
if( eman.pub.num_warnings != 0 ) {
if( fail ) {
vips_error( "VipsJpeg", "%s", vips_error_buffer() );
result = -1;
}
else {
vips_warn( "VipsJpeg",
_( "read gave %ld warnings" ),
eman.pub.num_warnings );
vips_warn( "VipsJpeg", "%s", vips_error_buffer() );
}
}
result |= readjpeg_free( jpeg );
return( result );
}
@ -967,8 +1056,9 @@ term_source (j_decompress_ptr cinfo)
*/
static void
buf_source (j_decompress_ptr cinfo, void *buf, size_t len)
readjpeg_buffer (ReadJpeg *jpeg, void *buf, size_t len)
{
j_decompress_ptr cinfo = &jpeg->cinfo;
InputBuffer *src;
/* The source object and input buffer are made permanent so that a series
@ -1001,44 +1091,36 @@ int
vips__jpeg_read_buffer( void *buf, size_t len, VipsImage *out,
gboolean header_only, int shrink, int fail )
{
struct jpeg_decompress_struct cinfo;
ErrorManager eman;
ReadJpeg *jpeg;
int result;
gboolean invert_pels;
/* Make jpeg dcompression object.
*/
cinfo.err = jpeg_std_error( &eman.pub );
eman.pub.error_exit = vips__new_error_exit;
eman.pub.output_message = vips__new_output_message;
eman.fp = NULL;
if( setjmp( eman.jmp ) ) {
/* Here for longjmp() from new_error_exit().
*/
jpeg_destroy_decompress( &cinfo );
if( !(jpeg = readjpeg_new( out, shrink, fail )) )
return( -1 );
if( setjmp( jpeg->eman.jmp ) ) {
(void) readjpeg_free( jpeg );
return( -1 );
}
jpeg_create_decompress( &cinfo );
/* Make input.
/* Set input to buffer.
*/
buf_source( &cinfo, buf, len );
readjpeg_buffer( jpeg, buf, len );
/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
*/
jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );
jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 1, 0xffff );
jpeg_save_markers( &jpeg->cinfo, JPEG_APP0 + 2, 0xffff );
/* Convert!
*/
result = read_jpeg_header( &cinfo, out, &invert_pels, 1 );
result = read_jpeg_header( jpeg );
if( !header_only && !result )
result = read_jpeg_image( &cinfo, out, invert_pels );
result = read_jpeg_image( jpeg );
/* Close and tidy.
*/
jpeg_destroy_decompress( &cinfo );
result |= readjpeg_free( jpeg );
return( result );
}