From ab0f72db621a8b5330197e537c1c6f1a0da4c72f Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Thu, 24 Nov 2011 21:53:40 +0000 Subject: [PATCH] new jpeg loader works --- TODO | 13 +- libvips/file/file.c | 161 +++++++----- libvips/file/jpegload.c | 486 ++++++++++++++++++++++++++++++++++-- libvips/include/vips/file.h | 17 +- libvips/iofuncs/init.c | 1 + 5 files changed, 585 insertions(+), 93 deletions(-) diff --git a/TODO b/TODO index 410a06c9..d93368b6 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,15 @@ -- file.c has lazy open stuff in, needs work though +- get image.c using the new system - how do we handle loaders that don't need to be lazy (eg. tiled tiff load) + move format/* to deprecated + + try jpeg save + + try tiff load + + + + +- return missing just after read_xmp() in master? diff --git a/libvips/file/file.c b/libvips/file/file.c index b3581676..7a1427aa 100644 --- a/libvips/file/file.c +++ b/libvips/file/file.c @@ -36,6 +36,7 @@ #include #include +#include /** * SECTION: file @@ -245,20 +246,24 @@ vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf ) vips_buf_appends( buf, ") " ); } - vips_buf_appendf( buf, "priority=%d ", class->priority ); + vips_buf_appendf( buf, "priority=%d", class->priority ); } static void vips_file_class_init( VipsFileClass *class ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + object_class->nickname = "file"; object_class->description = _( "load and save image files" ); object_class->print_class = vips_file_print_class; - VIPS_ARG_STRING( class, "filename", 12, + VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "File filename" ), VIPS_ARGUMENT_REQUIRED_INPUT, @@ -331,6 +336,16 @@ vips_file_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_file_load, VIPS_TYPE_FILE ); +static void +vips_file_load_dispose( GObject *gobject ) +{ + VipsFileLoad *load = VIPS_FILE_LOAD( gobject ); + + VIPS_UNREF( load->real ); + + G_OBJECT_CLASS( vips_file_load_parent_class )->dispose( gobject ); +} + static void vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf ) { @@ -338,24 +353,19 @@ vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf ) VIPS_OBJECT_CLASS( vips_file_load_parent_class )-> print_class( object_class, buf ); - vips_buf_appends( buf, ", " ); if( class->is_a ) - vips_buf_appends( buf, "is_a " ); - - /* - if( class->header ) - vips_buf_appends( buf, "header " ); - if( class->load ) - vips_buf_appends( buf, "load " ); - */ - + vips_buf_appends( buf, ", is_a" ); if( class->get_flags ) - vips_buf_appends( buf, "get_flags " ); + vips_buf_appends( buf, ", get_flags" ); + if( class->header ) + vips_buf_appends( buf, ", header" ); + if( class->load ) + vips_buf_appends( buf, ", load" ); } static size_t -disc_threshold( void ) +vips_get_disc_threshold( void ) { static gboolean done = FALSE; static size_t threshold; @@ -375,42 +385,13 @@ disc_threshold( void ) if( vips__disc_threshold ) threshold = vips__parse_size( vips__disc_threshold ); - VIPS_DEBUG_MSG( "disc_threshold: %zd bytes\n", threshold ); + VIPS_DEBUG_MSG( "vips_get_disc_threshold: " + "%zd bytes\n", threshold ); } return( threshold ); } -/* Make the real underlying image: either a direct disc file, or a temp file - * somewhere. - */ -static int -vips_file_load_new_real( VipsFileLoad *load ) -{ - /* We open via disc if: - * - 'disc' is set - * - disc_threshold() has not been set to zero - * - the format does not support lazy read - * - the uncompressed image will be larger than disc_threshold() - */ - load->real = NULL; - if( lazy->disc && - disc_threshold() && - !(vips_format_get_flags( lazy->format, lazy->filename ) & - VIPS_FORMAT_PARTIAL) && - VIPS_IMAGE_SIZEOF_IMAGE( lazy->image ) > disc_threshold() ) - if( !(real = vips_image_new_disc_temp( "%s.v" )) ) - return( NULL ); - - /* Otherwise, fall back to a "p". - */ - if( !real && - !(real = vips_image_new()) ) - return( NULL ); - - return( real ); -} - /* Our start function ... do the lazy open, if necessary, and return a region * on the new image. */ @@ -421,8 +402,32 @@ vips_file_load_start_cb( VipsImage *out, void *a, void *dummy ) VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( a ); if( !load->real ) { - if( vips_file_load_new_real( load ) || - class->load( load ) || + const size_t disc_threshold = vips_get_disc_threshold(); + const size_t image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out ); + + /* We open via disc if: + * - 'disc' is set + * - disc-threshold has not been set to zero + * - the format does not support lazy read + * - the uncompressed image will be larger than + * vips_get_disc_threshold() + */ + if( load->disc && + disc_threshold && + (load->flags & VIPS_FORMAT_PARTIAL) && + image_size > disc_threshold ) + if( !(load->real = vips_image_new_disc_temp( "%s.v" )) ) + return( NULL ); + + /* Otherwise, fall back to a "p". + */ + if( !load->real && + !(load->real = vips_image_new()) ) + return( NULL ); + + /* Read the image in. + */ + if( class->load( load ) || vips_image_pio_input( load->real ) ) { VIPS_UNREF( load->real ); return( NULL ); @@ -463,10 +468,9 @@ vips_file_load_build( VipsObject *object ) g_object_set( object, "out", vips_image_new(), NULL ); - if( class->get_flags ) - g_object_set( load, - "flags", class->get_flags( load ), - NULL ); + if( class->get_flags && + class->get_flags( load ) ) + return( -1 ); if( VIPS_OBJECT_CLASS( vips_file_load_parent_class )-> build( object ) ) @@ -481,14 +485,14 @@ vips_file_load_build( VipsObject *object ) return( -1 ); vips_demand_hint( load->out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - /* Then 'start' creates the real image and 'gen' paints 'out' with - * pixels from the real image on demand. + /* Then 'start' creates the real image and 'gen' fetches pixels for + * 'out' from real on demand. */ if( vips_image_generate( load->out, vips_file_load_start_cb, vips_file_load_generate_cb, vips_stop_one, - lazy, NULL ) ) + load, NULL ) ) return( -1 ); return( 0 ); @@ -497,13 +501,24 @@ vips_file_load_build( VipsObject *object ) static void vips_file_load_class_init( VipsFileLoadClass *class ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + gobject_class->dispose = vips_file_load_dispose; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + object_class->nickname = "fileload"; object_class->description = _( "file loaders" ); object_class->print_class = vips_file_load_print_class; object_class->build = vips_file_load_build; + VIPS_ARG_IMAGE( class, "out", 2, + _( "Output" ), + _( "Output image" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsFileLoad, out ) ); + VIPS_ARG_ENUM( class, "flags", 6, _( "Flags" ), _( "Flags for this file" ), @@ -511,24 +526,19 @@ vips_file_load_class_init( VipsFileLoadClass *class ) G_STRUCT_OFFSET( VipsFileLoad, flags ), VIPS_TYPE_FILE_FLAGS, VIPS_FILE_NONE ); - VIPS_ARG_IMAGE( class, "out", 1, - _( "Output" ), - _( "Output image" ), - VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsFileLoad, out ) ); - - VIPS_ARG_BOOL( class, "memory", 6, - _( "Memory" ), - _( "Open to memory" ), + VIPS_ARG_BOOL( class, "disc", 7, + _( "Disc" ), + _( "Open to disc" ), VIPS_ARGUMENT_OPTIONAL_INPUT, - G_STRUCT_OFFSET( VipsFileLoad, memory ), - FALSE ); + G_STRUCT_OFFSET( VipsFileLoad, disc ), + TRUE ); } static void -vips_file_load_init( VipsFileLoad *object ) +vips_file_load_init( VipsFileLoad *load ) { + load->disc = TRUE; } /* Can this file open this file? @@ -619,14 +629,18 @@ vips_file_save_build( VipsObject *object ) static void vips_file_save_class_init( VipsFileSaveClass *class ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + object_class->nickname = "filesave"; object_class->description = _( "file savers" ); object_class->print_class = vips_file_save_print_class; object_class->build = vips_file_save_build; - VIPS_ARG_IMAGE( class, "in", 1, + VIPS_ARG_IMAGE( class, "in", 0, _( "Input" ), _( "Image to save" ), VIPS_ARGUMENT_REQUIRED_INPUT, @@ -740,3 +754,16 @@ vips_file_write( VipsImage *in, const char *filename, ... ) return( result ); } +/* Called from iofuncs to init all operations in this dir. Use a plugin system + * instead? + */ +void +vips_file_operation_init( void ) +{ + extern GType vips_file_load_jpeg_get_type( void ); + +#ifdef HAVE_JPEG + vips_file_load_jpeg_get_type(); +#endif /*HAVE_JPEG*/ +} + diff --git a/libvips/file/jpegload.c b/libvips/file/jpegload.c index 0954c13e..8ef15a37 100644 --- a/libvips/file/jpegload.c +++ b/libvips/file/jpegload.c @@ -123,12 +123,49 @@ typedef struct _VipsFileLoadJpeg { */ gboolean fail; + /* For some jpeg CMYK formats we have to invert pels on read. + */ + gboolean invert_pels; + } VipsFileLoadJpeg; -typedef VipsFileLoadJpegClass VipsFileLoadJpeg; +typedef VipsFileLoadClass VipsFileLoadJpegClass; G_DEFINE_TYPE( VipsFileLoadJpeg, vips_file_load_jpeg, VIPS_TYPE_FILE_LOAD ); +static int +vips_file_load_jpeg_build( VipsObject *object ) +{ + VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object; + + if( jpeg->shrink != 1 && + jpeg->shrink != 2 && + jpeg->shrink != 4 && + jpeg->shrink != 8 ) { + vips_error( "VipsFormatLoadJpeg", + _( "bad shrink factor %d" ), jpeg->shrink ); + return( -1 ); + } + + if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static gboolean +vips_file_load_jpeg_is_a( const char *filename ) +{ + unsigned char buf[2]; + + if( vips__get_bytes( filename, buf, 2 ) ) + if( (int) buf[0] == 0xff && (int) buf[1] == 0xd8 ) + return( TRUE ); + + return( FALSE ); +} + /* Define a new error handler for when we bomb out. */ typedef struct { @@ -184,6 +221,54 @@ new_error_exit( j_common_ptr cinfo ) longjmp( eman->jmp, 1 ); } +/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to + * do 255-pel. + */ +static int +vips_file_load_jpeg_read_header( VipsFileLoadJpeg *jpeg, + struct jpeg_decompress_struct *cinfo, VipsImage *out ) +{ + int type; + + /* Read JPEG header. libjpeg will set out_color_space sanely for us + * for YUV YCCK etc. + */ + jpeg_read_header( cinfo, TRUE ); + cinfo->scale_denom = jpeg->shrink; + cinfo->scale_num = 1; + jpeg_calc_output_dimensions( cinfo ); + + switch( cinfo->out_color_space ) { + case JCS_GRAYSCALE: + type = IM_TYPE_B_W; + break; + + case JCS_CMYK: + type = IM_TYPE_CMYK; + /* Photoshop writes CMYK JPEG inverted :-( Maybe this is a + * way to spot photoshop CMYK JPGs. + */ + if( cinfo->saw_Adobe_marker ) + jpeg->invert_pels = TRUE; + break; + + case JCS_RGB: + default: + type = IM_TYPE_sRGB; + break; + } + + /* Set VIPS header. + */ + vips_image_init_fields( out, + cinfo->output_width, cinfo->output_height, + cinfo->output_components, + VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, type, + 1.0, 1.0 ); + + return( 0 ); +} + #ifdef HAVE_EXIF #ifdef DEBUG_VERBOSE /* Print exif for debugging ... hacked from exif-0.6.9/actions.c @@ -485,47 +570,416 @@ attach_thumbnail( IMAGE *im, ExifData *ed ) } #endif /*HAVE_EXIF*/ - static int -vips_file_load_jpeg_build( VipsObject *object ) +read_exif( IMAGE *im, void *data, int data_length ) { - VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) object; + char *data_copy; - if( jpeg->shrink != 1 && - jpeg->shrink != 2 && - jpeg->shrink != 4 && - jpeg->shrink != 8 ) { - vips_error( "VipsFormatLoadJpeg", - _( "bad shrink factor %d" ), jpeg->shrink ); + /* Only use the first one. + */ + if( im_header_get_typeof( im, IM_META_EXIF_NAME ) ) { +#ifdef DEBUG + printf( "read_exif: second EXIF block, ignoring\n" ); +#endif /*DEBUG*/ + + return( 0 ); + } + +#ifdef DEBUG + printf( "read_exif: attaching %d bytes of exif\n", data_length ); +#endif /*DEBUG*/ + + /* Always attach a copy of the unparsed exif data. + */ + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( im, IM_META_EXIF_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); return( -1 ); } - if( VIPS_OBJECT_CLASS( vips_file_load_jpeg_parent_class )-> - build( object ) ) +#ifdef HAVE_EXIF +{ + ExifData *ed; + + if( !(ed = exif_data_new_from_data( data, data_length )) ) return( -1 ); + if( ed->size > 0 ) { + VipsExif ve; +#ifdef DEBUG_VERBOSE + show_tags( ed ); + show_values( ed ); +#endif /*DEBUG_VERBOSE*/ + + /* Attach informational fields for what we find. + + FIXME ... better to have this in the UI layer? + + Or we could attach non-human-readable tags here (int, + double etc) and then move the human stuff to the UI + layer? + + */ + ve.image = im; + ve.ed = ed; + exif_data_foreach_content( ed, + (ExifDataForeachContentFunc) attach_exif_content, &ve ); + + /* Look for resolution fields and use them to set the VIPS + * xres/yres fields. + */ + set_vips_resolution( im, ed ); + + attach_thumbnail( im, ed ); + } + + exif_data_free( ed ); +} +#endif /*HAVE_EXIF*/ return( 0 ); } +static int +read_xmp( IMAGE *im, void *data, int data_length ) +{ + char *data_copy; + + /* XMP sections start "http". Only use the first one. + */ + if( im_header_get_typeof( im, VIPS_META_XMP_NAME ) ) { +#ifdef DEBUG + printf( "read_xmp: second XMP block, ignoring\n" ); +#endif /*DEBUG*/ + + return( 0 ); + } + +#ifdef DEBUG + printf( "read_xmp: attaching %d bytes of XMP\n", data_length ); +#endif /*DEBUG*/ + + /* Always attach a copy of the unparsed exif data. + */ + if( !(data_copy = im_malloc( NULL, data_length )) ) + return( -1 ); + memcpy( data_copy, data, data_length ); + if( im_meta_set_blob( im, VIPS_META_XMP_NAME, + (im_callback_fn) im_free, data_copy, data_length ) ) { + im_free( data_copy ); + return( -1 ); + } + + return( 0 ); +} + +/* Number of app2 sections we can capture. Each one can be 64k, so 6400k should + * be enough for anyone (haha). + */ +#define MAX_APP2_SECTIONS (100) + +static int +vips_file_load_jpeg_meta( VipsFileLoadJpeg *jpeg, + struct jpeg_decompress_struct *cinfo, VipsImage *out ) +{ + /* Capture app2 sections here for assembly. + */ + void *app2_data[MAX_APP2_SECTIONS] = { 0 }; + int app2_data_length[MAX_APP2_SECTIONS] = { 0 }; + int data_length; + jpeg_saved_marker_ptr p; + int i; + + /* Interlaced jpegs need lots of memory to read, so our caller needs + * to know. + */ + vips_image_set_int( out, "jpeg-multiscan", + jpeg_has_multiple_scans( cinfo ) ); + + /* Look for EXIF and ICC profile. + */ + for( p = cinfo->marker_list; p; p = p->next ) { +#ifdef DEBUG +{ + printf( "vips_file_load_jpeg_read_header: " + "seen %d bytes of APP%d\n", + p->data_length, + p->marker - JPEG_APP0 ); + + for( i = 0; i < 10; i++ ) + printf( "\t%d) '%c' (%d)\n", + i, p->data[i], p->data[i] ); +} +#endif /*DEBUG*/ + + switch( p->marker ) { + case JPEG_APP0 + 1: + /* Possible EXIF or XMP data. + */ + if( p->data_length > 4 && + im_isprefix( "Exif", (char *) p->data ) && + read_exif( out, p->data, p->data_length ) ) + return( -1 ); + + if( p->data_length > 4 && + im_isprefix( "http", (char *) p->data ) && + read_xmp( out, p->data, p->data_length ) ) + return( -1 ); + + break; + + case JPEG_APP0 + 2: + /* ICC profile. + */ + if( p->data_length > 14 && + im_isprefix( "ICC_PROFILE", + (char *) p->data ) ) { + /* cur_marker numbers from 1, according to + * spec. + */ + int cur_marker = p->data[12] - 1; + + if( cur_marker >= 0 && + cur_marker < MAX_APP2_SECTIONS ) { + app2_data[cur_marker] = p->data + 14; + app2_data_length[cur_marker] = + p->data_length - 14; + } + } + break; + + default: + break; + } + } + + /* Assemble ICC sections. + */ + data_length = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) + data_length += app2_data_length[i]; + if( data_length ) { + unsigned char *data; + int x; + +#ifdef DEBUG + printf( "vips_file_load_jpeg_read_header: " + "assembled %d byte ICC profile\n", + data_length ); +#endif /*DEBUG*/ + + data = g_malloc( data_length ); + x = 0; + for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) { + memcpy( data + x, app2_data[i], app2_data_length[i] ); + x += app2_data_length[i]; + } + vips_image_set_blob( out, VIPS_META_ICC_NAME, + (VipsCallbackFn) g_free, data, data_length ); + } + + return( 0 ); +} + +/* Read just the image header into ->out. + */ +static int +vips_file_load_jpeg_header( VipsFileLoad *load ) +{ + VipsFile *file = VIPS_FILE( load ); + VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load; + + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + int result; + + /* Make jpeg dcompression object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ + if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) ) + return( -1 ); + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* 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 ); + + /* Convert! + */ + result = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->out ); + + /* Get extra metadata too. + */ + if( !result ) + result = vips_file_load_jpeg_meta( jpeg, &cinfo, load->out ); + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + return( result ); +} + +/* Read a cinfo to a VIPS image. + */ +static int +vips_file_load_jpeg_read_image( VipsFileLoadJpeg *jpeg, + struct jpeg_decompress_struct *cinfo, VipsImage *out ) +{ + int x, y, sz; + JSAMPROW row_pointer[1]; + + /* Check VIPS. + */ + if( vips_image_wio_output( out ) ) + return( -1 ); + + /* Get size of output line and make a buffer. + */ + sz = cinfo->output_width * cinfo->output_components; + row_pointer[0] = (JSAMPLE *) (*cinfo->mem->alloc_large) + ( (j_common_ptr) cinfo, JPOOL_IMAGE, sz ); + + /* Start up decompressor. + */ + jpeg_start_decompress( cinfo ); + + /* Process image. + */ + for( y = 0; y < 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( 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] ) ) + return( -1 ); + } + + /* Stop decompressor. + */ + jpeg_finish_decompress( cinfo ); + + return( 0 ); +} + +static int +vips_file_load_jpeg_load( VipsFileLoad *load ) +{ + VipsFile *file = VIPS_FILE( load ); + VipsFileLoadJpeg *jpeg = (VipsFileLoadJpeg *) load; + + struct jpeg_decompress_struct cinfo; + ErrorManager eman; + FILE *fp; + int result; + + /* Make jpeg dcompression object. + */ + cinfo.err = jpeg_std_error( &eman.pub ); + eman.pub.error_exit = new_error_exit; + eman.pub.output_message = new_output_message; + eman.fp = NULL; + if( setjmp( eman.jmp ) ) { + /* Here for longjmp() from new_error_exit(). + */ + jpeg_destroy_decompress( &cinfo ); + + return( -1 ); + } + jpeg_create_decompress( &cinfo ); + + /* Make input. + */ + if( !(fp = vips__file_open_read( file->filename, NULL, FALSE )) ) + return( -1 ); + eman.fp = fp; + jpeg_stdio_src( &cinfo, fp ); + + /* Convert! + */ + result = vips_file_load_jpeg_read_header( jpeg, &cinfo, load->real ); + if( !result ) + result = vips_file_load_jpeg_read_image( jpeg, + &cinfo, load->real ); + + /* Close and tidy. + */ + fclose( fp ); + eman.fp = NULL; + jpeg_destroy_decompress( &cinfo ); + + if( eman.pub.num_warnings != 0 ) { + if( jpeg->fail ) { + vips_error( "VipsFileLoadJpeg", + "%s", vips_error_buffer() ); + result = -1; + } + else { + vips_warn( "VipsFileLoadJpeg", + _( "read gave %ld warnings" ), + eman.pub.num_warnings ); + vips_warn( "VipsFileLoadJpeg", + "%s", vips_error_buffer() ); + } + } + + return( result ); +} + static void vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class ) { + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFileLoadClass *load_class = (VipsFileLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; object_class->nickname = "jpegload"; object_class->description = _( "load jpeg from file" ); object_class->build = vips_file_load_jpeg_build; - VIPS_ARG_INT( class, "shrink", 5, + load_class->is_a = vips_file_load_jpeg_is_a; + load_class->header = vips_file_load_jpeg_header; + load_class->load = vips_file_load_jpeg_load; + + VIPS_ARG_INT( class, "shrink", 10, _( "Shrink" ), _( "Shrink factor on load" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ), 1, 16, 1 ); - VIPS_ARG_BOOL( class, "fail", 6, + VIPS_ARG_BOOL( class, "fail", 11, _( "Fail" ), _( "Fail on first warning" ), VIPS_ARGUMENT_OPTIONAL_INPUT, @@ -535,10 +989,8 @@ vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class ) } static void -vips_file_load_jpeg_init( VipsFileLoadJpeg *object ) +vips_file_load_jpeg_init( VipsFileLoadJpeg *jpeg ) { jpeg->shrink = 1; } - - diff --git a/libvips/include/vips/file.h b/libvips/include/vips/file.h index dd0211b3..61c665af 100644 --- a/libvips/include/vips/file.h +++ b/libvips/include/vips/file.h @@ -73,7 +73,7 @@ typedef struct _VipsFileClass { int priority; /* Null-terminated list of recommended suffixes, eg. ".tif", ".tiff". - * This is used by both load and save, so it's in the base class. + * This can be used by both load and save, so it's in the base class. */ const char **suffs; @@ -113,9 +113,9 @@ typedef struct _VipsFileLoad { VipsFile parent_object; /*< public >*/ - /* Open to memory (default is to open via disc). + /* Open to disc (default is to open to memory). */ - gboolean memory; + gboolean disc; /* Flags read from the file. */ @@ -125,7 +125,8 @@ typedef struct _VipsFileLoad { */ VipsImage *out; - /* The behind-the-scenes real image we decompress to. + /* The behind-the-scenes real image we decompress to. This can be a + * disc file or a memory buffer. */ VipsImage *real; @@ -140,15 +141,15 @@ typedef struct _VipsFileLoadClass { */ gboolean (*is_a)( const char * ); - /* Get the flags for this file in this file. + /* Get the flags for this file. */ - VipsFileFlags (*get_flags)( VipsFileLoad * ); + int (*get_flags)( VipsFileLoad * ); /* Read the header into @out. */ int (*header)( VipsFileLoad * ); - /* Read the whole image into @out. + /* Read the whole image into real. It gets copied to out later. */ int (*load)( VipsFileLoad * ); @@ -199,6 +200,8 @@ const char *vips_file_find_save( const char *filename ); int vips_file_read( const char *filename, VipsImage **out, ... ); int vips_file_write( VipsImage *in, const char *filename, ... ); +void vips_file_operation_init( void ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/init.c b/libvips/iofuncs/init.c index 63099d86..a51079a1 100644 --- a/libvips/iofuncs/init.c +++ b/libvips/iofuncs/init.c @@ -231,6 +231,7 @@ vips_init( const char *argv0 ) */ vips_arithmetic_operation_init(); vips_conversion_operation_init(); + vips_file_operation_init(); /* Load up any plugins in the vips libdir. We don't error on failure, * it's too annoying to have VIPS refuse to start because of a broken