diff --git a/TODO b/TODO index 130a6454..410a06c9 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,6 @@ -- move all the haeder / lazy load / lazy save stuff from image.c into file.c +- file.c has lazy open stuff in, needs work though - can common up a lot of load/save from the *2vips.c etc. into FileLoad, - hopefully - - format from name should retur a char*, a recommended load operation + how do we handle loaders that don't need to be lazy (eg. tiled tiff load) diff --git a/libvips/file/Makefile.am b/libvips/file/Makefile.am index b797472c..2bdac6e1 100644 --- a/libvips/file/Makefile.am +++ b/libvips/file/Makefile.am @@ -1,6 +1,7 @@ noinst_LTLIBRARIES = libfile.la libfile_la_SOURCES = \ + jpegload.c \ file.c INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ diff --git a/libvips/file/file.c b/libvips/file/file.c index 178168b3..b3581676 100644 --- a/libvips/file/file.c +++ b/libvips/file/file.c @@ -220,7 +220,58 @@ G_DEFINE_TYPE( VipsFileTiff, vips_file_tiff, VIPS_TYPE_FILE ); * */ -/* To iterate over supported files, we build a temp list of subclasses of +/* Abstract base class for image files. + */ + +G_DEFINE_ABSTRACT_TYPE( VipsFile, vips_file, VIPS_TYPE_OPERATION ); + +static void +vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf ) +{ + VipsFileClass *class = VIPS_FILE_CLASS( object_class ); + const char **p; + + VIPS_OBJECT_CLASS( vips_file_parent_class )-> + print_class( object_class, buf ); + vips_buf_appends( buf, ", " ); + + if( class->suffs ) { + vips_buf_appends( buf, "(" ); + for( p = class->suffs; *p; p++ ) { + vips_buf_appendf( buf, "%s", *p ); + if( p[1] ) + vips_buf_appends( buf, ", " ); + } + vips_buf_appends( buf, ") " ); + } + + vips_buf_appendf( buf, "priority=%d ", class->priority ); + +} + +static void +vips_file_class_init( VipsFileClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + + 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, + _( "Filename" ), + _( "File filename" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsFile, filename ), + NULL ); +} + +static void +vips_file_init( VipsFile *object ) +{ +} + +/* To iterate over supported files we build a temp list of subclasses of * VipsFile, sort by priority, iterate, and free. */ @@ -275,94 +326,171 @@ vips_file_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) return( result ); } -/* Abstract base class for image files. - */ - -G_DEFINE_ABSTRACT_TYPE( VipsFile, vips_file, VIPS_TYPE_OPERATION ); - -static void -vips_file_print_class( VipsObjectClass *object_class, VipsBuf *buf ) -{ - VipsFileClass *class = VIPS_FILE_CLASS( object_class ); - const char **p; - - VIPS_OBJECT_CLASS( vips_file_parent_class )-> - print_class( object_class, buf ); - vips_buf_appends( buf, ", " ); - - if( class->suffs ) { - vips_buf_appends( buf, "(" ); - for( p = class->suffs; *p; p++ ) { - vips_buf_appendf( buf, "%s", *p ); - if( p[1] ) - vips_buf_appends( buf, ", " ); - } - vips_buf_appends( buf, ") " ); - } - - vips_buf_appends( buf, "priority=%d ", class->priority ); - - if( class->is_a ) - vips_buf_appends( buf, "is_a " ); -} - -static void -vips_file_class_init( VipsFileClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - - object_class->nickname = "file"; - object_class->description = _( "file file support" ); - object_class->print_class = vips_file_print_class; - - VIPS_ARG_STRING( class, "filename", 12, - _( "Filename" ), - _( "File filename" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsFile, filename ), - NULL ); -} - -static void -vips_file_init( VipsFile *object ) -{ -} - /* Abstract base class for image load. */ -G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_files_load, VIPS_TYPE_FILE ); +G_DEFINE_ABSTRACT_TYPE( VipsFileLoad, vips_file_load, VIPS_TYPE_FILE ); static void vips_file_load_print_class( VipsObjectClass *object_class, VipsBuf *buf ) { VipsFileLoadClass *class = VIPS_FILE_LOAD_CLASS( object_class ); - const char **p; 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 " ); + */ + if( class->get_flags ) vips_buf_appends( buf, "get_flags " ); } +static size_t +disc_threshold( void ) +{ + static gboolean done = FALSE; + static size_t threshold; + + if( !done ) { + const char *env; + + done = TRUE; + + /* 100mb default. + */ + threshold = 100 * 1024 * 1024; + + if( (env = g_getenv( "IM_DISC_THRESHOLD" )) ) + threshold = vips__parse_size( env ); + + if( vips__disc_threshold ) + threshold = vips__parse_size( vips__disc_threshold ); + + VIPS_DEBUG_MSG( "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. + */ +static void * +vips_file_load_start_cb( VipsImage *out, void *a, void *dummy ) +{ + VipsFileLoad *load = VIPS_FILE_LOAD( a ); + VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( a ); + + if( !load->real ) { + if( vips_file_load_new_real( load ) || + class->load( load ) || + vips_image_pio_input( load->real ) ) { + VIPS_UNREF( load->real ); + return( NULL ); + } + } + + return( vips_region_new( load->real ) ); +} + +/* Just pointer-copy. + */ +static int +vips_file_load_generate_cb( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + + VipsRect *r = &or->valid; + + /* Ask for input we need. + */ + if( vips_region_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( vips_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + static int vips_file_load_build( VipsObject *object ) { - VipsFile *file = VIPS_FILE( object ); VipsFileLoad *load = VIPS_FILE_LOAD( object ); + VipsFileLoadClass *class = VIPS_FILE_LOAD_GET_CLASS( 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( VIPS_OBJECT_CLASS( vips_file_load_parent_class )-> build( object ) ) return( -1 ); + /* Read header fields to init the return image. THINSTRIP since this is + * probably a disc file. We can't tell yet whether we will be opening + * to memory, sadly, so we can't suggest ANY. + */ + if( class->header && + class->header( load ) ) + 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. + */ + if( vips_image_generate( load->out, + vips_file_load_start_cb, + vips_file_load_generate_cb, + vips_stop_one, + lazy, NULL ) ) + return( -1 ); + return( 0 ); } @@ -389,6 +517,13 @@ vips_file_load_class_init( VipsFileLoadClass *class ) VIPS_ARGUMENT_REQUIRED_OUTPUT, G_STRUCT_OFFSET( VipsFileLoad, out ) ); + VIPS_ARG_BOOL( class, "memory", 6, + _( "Memory" ), + _( "Open to memory" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsFileLoad, memory ), + FALSE ); + } static void @@ -402,32 +537,32 @@ static void * vips_file_load_new_from_file_sub( VipsFileLoadClass *load_class, const char *filename ) { + VipsFileClass *class = VIPS_FILE_CLASS( load_class ); + if( load_class->is_a ) { if( load_class->is_a( filename ) ) return( load_class ); } - else if( vips_filename_suffix_match( filename, load_class->suffs ) ) + else if( vips_filename_suffix_match( filename, class->suffs ) ) return( load_class ); return( NULL ); } /** - * vips_file_load_new_from_file: + * vips_file_find_load: * @filename: file to find a file for * - * Searches for a file you could use to load a file. Set some more options - * after this, then call _build() to actually load in the file. + * Searches for an operation you could use to load a file. * - * See also: vips_file_read(), vips_file_for_name(). + * See also: vips_file_read(). * - * Returns: a file on success, %NULL on error + * Returns: the nmae of an operation on success, %NULL on error */ -VipsFileLoad * -vips_file_load_new_from_file( const char *filename ) +const char * +vips_file_find_load( const char *filename ) { VipsFileLoadClass *load_class; - VipsFileLoad *load; if( !vips_existsf( "%s", filename ) ) { vips_error( "VipsFileLoad", @@ -440,21 +575,11 @@ vips_file_load_new_from_file( const char *filename ) (VipsSListMap2Fn) vips_file_load_new_from_file_sub, (void *) filename, NULL )) ) { vips_error( "VipsFileLoad", - _( "file \"%s\" not a known file" ), name ); + _( "file \"%s\" not a known file" ), filename ); return( NULL ); } - load = VIPS_FILE_LOAD( - g_object_new( G_TYPE_FROM_CLASS( load_class ), NULL ) ); - - /* May as well set flags here, should be quick. - */ - if( load_class->get_flags ) - g_object_set( load, - "flags", load_class->get_flags( load ), - NULL ); - - return( load ); + return( G_OBJECT_CLASS_NAME( load_class ) ); } /* Abstract base class for image savers. @@ -465,22 +590,24 @@ G_DEFINE_ABSTRACT_TYPE( VipsFileSave, vips_file_save, VIPS_TYPE_FILE ); static void vips_file_save_print_class( VipsObjectClass *object_class, VipsBuf *buf ) { - VipsFileSaveClass *class = VIPS_FILE_SAVE_CLASS( object_class ); - const char **p; - VIPS_OBJECT_CLASS( vips_file_save_parent_class )-> print_class( object_class, buf ); vips_buf_appends( buf, ", " ); + /* + VipsFileSaveClass *class = VIPS_FILE_SAVE_CLASS( object_class ); if( class->save ) vips_buf_appends( buf, "save " ); + */ } static int vips_file_save_build( VipsObject *object ) { + /* VipsFile *file = VIPS_FILE( object ); VipsFileSave *save = VIPS_FILE_SAVE( object ); + */ if( VIPS_OBJECT_CLASS( vips_file_save_parent_class )-> build( object ) ) @@ -507,7 +634,7 @@ vips_file_save_class_init( VipsFileSaveClass *class ) } static void -vips_file_save_init( VipsFile *object ) +vips_file_save_init( VipsFileSave *object ) { } @@ -517,30 +644,28 @@ static void * vips_file_save_new_from_filename_sub( VipsFileSaveClass *save_class, const char *filename ) { - VipsFileClass *file = VIPS_FILE_CLASS( save_class ); + VipsFileClass *class = VIPS_FILE_CLASS( save_class ); - if( save_class->save && - vips_filename_suffix_match( filename, file->suffs ) ) + if( vips_filename_suffix_match( filename, class->suffs ) ) return( save_class ); return( NULL ); } /** - * vips_file_save_new_from_filename: + * vips_file_find_save: * @filename: name to find a file for * - * Searches for a file you could use to save a file. + * Searches for an operation you could use to save a file. * - * See also: vips_file_write(), vips_file_for_file(). + * See also: vips_file_write(). * - * Returns: a file on success, %NULL on error + * Returns: the name of an operation on success, %NULL on error */ -VipsFileSave * -vips_file_save_new_from_filename( const char *filename ) +const char * +vips_file_find_save( const char *filename ) { VipsFileSaveClass *save_class; - VipsFileSave *save; if( !(save_class = (VipsFileSaveClass *) vips_file_map( "VipsFileSave", @@ -553,33 +678,37 @@ vips_file_save_new_from_filename( const char *filename ) return( NULL ); } - save = VIPS_FILE_SAVE( - g_object_new( G_TYPE_FROM_CLASS( save_class ), NULL ) ); - - return( save ); + return( G_OBJECT_CLASS_NAME( save_class ) ); } /** * vips_file_read: * @filename: file to load - * @out: write the file to this image + * @out: output image + * @...: %NULL-terminated list of optional named arguments * - * Searches for a file for this file, then loads the file into @out. + * Loads @filename into @out using the loader recommended by + * vips_file_find_load(). * * See also: vips_file_write(). * * Returns: 0 on success, -1 on error */ int -vips_file_read( const char *filename, VipsImage *out ) +vips_file_read( const char *filename, VipsImage **out, ... ) { - VipsFileLoad *load; + const char *operation; + va_list ap; + int result; - if( !(load = vips_file_load_new_from_file( filename )) ) + if( !(operation = vips_file_find_load( filename )) ) return( -1 ); - g_object_unref( load ); - return( 0 ); + va_start( ap, out ); + result = vips_call_split( operation, ap, filename, out ); + va_end( ap ); + + return( result ); } /** @@ -587,21 +716,27 @@ vips_file_read( const char *filename, VipsImage *out ) * @in: image to write * @filename: file to write to * - * Searches for a file for this name, then saves @im to it. + * Saves @in to @filename using the saver recommended by + * vips_file_find_save(). * * See also: vips_file_read(). * * Returns: 0 on success, -1 on error */ int -vips_file_write( VipsImage *in, const char *filename ) +vips_file_write( VipsImage *in, const char *filename, ... ) { - VipsFileClass *file; + const char *operation; + va_list ap; + int result; - if( !(file = vips_file_for_name( filename )) || - file->save( in, filename ) ) + if( !(operation = vips_file_find_save( filename )) ) return( -1 ); - return( 0 ); + va_start( ap, filename ); + result = vips_call_split( operation, ap, in, filename ); + va_end( ap ); + + return( result ); } diff --git a/libvips/file/jpegload.c b/libvips/file/jpegload.c new file mode 100644 index 00000000..0954c13e --- /dev/null +++ b/libvips/file/jpegload.c @@ -0,0 +1,544 @@ +/* load jpeg from a file + * + * 28/11/03 JC + * - better no-overshoot on tile loop + * 12/11/04 + * - better demand size choice for eval + * 30/6/05 JC + * - update im_error()/im_warn() + * - now loads and saves exif data + * 30/7/05 + * - now loads ICC profiles + * - now saves ICC profiles from the VIPS header + * 24/8/05 + * - jpeg load sets vips xres/yres from exif, if possible + * - jpeg save sets exif xres/yres from vips, if possible + * 29/8/05 + * - cut from old vips_jpeg.c + * 13/10/06 + * - add +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include + +#ifdef HAVE_EXIF +#ifdef UNTAGGED_EXIF +#include +#include +#include +#include +#else /*!UNTAGGED_EXIF*/ +#include +#include +#include +#include +#endif /*UNTAGGED_EXIF*/ +#endif /*HAVE_EXIF*/ + +#include +#include +#include + +/* jpeglib includes jconfig.h, which can define HAVE_STDLIB_H ... which we + * also define. Make sure it's turned off. + */ +#ifdef HAVE_STDLIB_H +#undef HAVE_STDLIB_H +#endif /*HAVE_STDLIB_H*/ + +#include +#include + +typedef struct _VipsFileLoadJpeg { + VipsFileLoad parent_object; + + /* Shrink by this much during load. + */ + int shrink; + + /* Fail on first warning. + */ + gboolean fail; + +} VipsFileLoadJpeg; + +typedef VipsFileLoadJpegClass VipsFileLoadJpeg; + +G_DEFINE_TYPE( VipsFileLoadJpeg, vips_file_load_jpeg, VIPS_TYPE_FILE_LOAD ); + +/* Define a new error handler for when we bomb out. + */ +typedef struct { + /* Public fields. + */ + struct jpeg_error_mgr pub; + + /* Private stuff for us. + */ + jmp_buf jmp; /* longjmp() here to get back to VIPS */ + FILE *fp; /* fclose() if non-NULL */ +} ErrorManager; + +/* New output message method - send to VIPS. + */ +METHODDEF(void) +new_output_message( j_common_ptr cinfo ) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message)( cinfo, buffer ); + im_error( "VipsFileLoadJpeg", _( "%s" ), buffer ); + +#ifdef DEBUG + printf( "VipsFileLoadJpeg: new_output_message: \"%s\"\n", buffer ); +#endif /*DEBUG*/ +} + +/* New error_exit handler. + */ +METHODDEF(void) +new_error_exit( j_common_ptr cinfo ) +{ + ErrorManager *eman = (ErrorManager *) cinfo->err; + +#ifdef DEBUG + printf( "VipsFileLoadJpeg: new_error_exit\n" ); +#endif /*DEBUG*/ + + /* Close the fp if necessary. + */ + if( eman->fp ) { + (void) fclose( eman->fp ); + eman->fp = NULL; + } + + /* Send the error message to VIPS. This method is overridden above. + */ + (*cinfo->err->output_message)( cinfo ); + + /* Jump back. + */ + longjmp( eman->jmp, 1 ); +} + +#ifdef HAVE_EXIF +#ifdef DEBUG_VERBOSE +/* Print exif for debugging ... hacked from exif-0.6.9/actions.c + */ +static void +show_tags( ExifData *data ) +{ + int i; + unsigned int tag; + const char *name; + + printf( "show EXIF tags:\n" ); + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + printf( "%-7.7s", exif_ifd_get_name( i ) ); + printf( "\n" ); + + for( tag = 0; tag < 0xffff; tag++ ) { + name = exif_tag_get_title( tag ); + if( !name ) + continue; + printf( " 0x%04x %-29.29s", tag, name ); + for( i = 0; i < EXIF_IFD_COUNT; i++ ) + if( exif_content_get_entry( data->ifd[i], tag ) ) + printf( " * " ); + else + printf( " - " ); + printf( "\n" ); + } +} + +static void +show_entry( ExifEntry *entry, void *client ) +{ + char exif_text[256]; + + printf( "%s", exif_tag_get_title( entry->tag ) ); + printf( "|" ); + printf( "%s", exif_entry_get_value( entry, exif_text, 256 ) ); + printf( "|" ); + printf( "%s", exif_format_get_name( entry->format ) ); + printf( "|" ); + printf( "%d bytes", entry->size ); + printf( "\n" ); +} + +static void +show_ifd( ExifContent *content, void *client ) +{ + exif_content_foreach_entry( content, show_entry, client ); + printf( "-\n" ); +} + +void +show_values( ExifData *data ) +{ + ExifByteOrder order; + + order = exif_data_get_byte_order( data ); + printf( "EXIF tags in '%s' byte order\n", + exif_byte_order_get_name( order ) ); + + printf( "%-20.20s", "Tag" ); + printf( "|" ); + printf( "%-58.58s", "Value" ); + printf( "\n" ); + + exif_data_foreach_content( data, show_ifd, NULL ); + + if( data->size ) + printf( "contains thumbnail of %d bytes\n", data->size ); +} +#endif /*DEBUG_VERBOSE*/ +#endif /*HAVE_EXIF*/ + +#ifdef HAVE_EXIF + +static int +vips_exif_get_int( ExifData *ed, + ExifEntry *entry, unsigned long component, int *out ) +{ + ExifByteOrder bo = exif_data_get_byte_order( ed ); + size_t sizeof_component = entry->size / entry->components; + size_t offset = component * sizeof_component; + + if( entry->format == EXIF_FORMAT_SHORT ) + *out = exif_get_short( entry->data + offset, bo ); + else if( entry->format == EXIF_FORMAT_SSHORT ) + *out = exif_get_sshort( entry->data + offset, bo ); + else if( entry->format == EXIF_FORMAT_LONG ) + /* This won't work for huge values, but who cares. + */ + *out = (int) exif_get_long( entry->data + offset, bo ); + else if( entry->format == EXIF_FORMAT_SLONG ) + *out = exif_get_slong( entry->data + offset, bo ); + else + return( -1 ); + + return( 0 ); +} + +static int +vips_exif_get_double( ExifData *ed, + ExifEntry *entry, unsigned long component, double *out ) +{ + ExifByteOrder bo = exif_data_get_byte_order( ed ); + size_t sizeof_component = entry->size / entry->components; + size_t offset = component * sizeof_component; + + if( entry->format == EXIF_FORMAT_RATIONAL ) { + ExifRational value; + + value = exif_get_rational( entry->data + offset, bo ); + *out = (double) value.numerator / value.denominator; + } + else if( entry->format == EXIF_FORMAT_SRATIONAL ) { + ExifSRational value; + + value = exif_get_srational( entry->data + offset, bo ); + *out = (double) value.numerator / value.denominator; + } + else + return( -1 ); + + return( 0 ); +} + +/* Save an exif value to a string in a way that we can restore. We only bother + * for the simple formats (that a client might try to change) though. + * + * Keep in sync with vips_exif_from_s() in vips2jpeg. + */ +static void +vips_exif_to_s( ExifData *ed, ExifEntry *entry, VipsBuf *buf ) +{ + unsigned long i; + int iv; + double dv; + char txt[256]; + + if( entry->format == EXIF_FORMAT_ASCII ) + vips_buf_appendf( buf, "%s ", entry->data ); + + else if( entry->components < 10 && + !vips_exif_get_int( ed, entry, 0, &iv ) ) { + for( i = 0; i < entry->components; i++ ) { + vips_exif_get_int( ed, entry, i, &iv ); + vips_buf_appendf( buf, "%d ", iv ); + } + } + else if( entry->components < 10 && + !vips_exif_get_double( ed, entry, 0, &dv ) ) { + for( i = 0; i < entry->components; i++ ) { + vips_exif_get_double( ed, entry, i, &dv ); + /* Need to be locale independent. + */ + g_ascii_dtostr( txt, 256, dv ); + vips_buf_appendf( buf, "%s ", txt ); + } + } + else + vips_buf_appendf( buf, "%s ", + exif_entry_get_value( entry, txt, 256 ) ); + + vips_buf_appendf( buf, "(%s, %s, %lu components, %d bytes)", + exif_entry_get_value( entry, txt, 256 ), + exif_format_get_name( entry->format ), + entry->components, + entry->size ); +} + +typedef struct _VipsExif { + VipsImage *image; + ExifData *ed; +} VipsExif; + +static void +attach_exif_entry( ExifEntry *entry, VipsExif *ve ) +{ + char name_txt[256]; + VipsBuf name = VIPS_BUF_STATIC( name_txt ); + char value_txt[256]; + VipsBuf value = VIPS_BUF_STATIC( value_txt ); + + vips_buf_appendf( &name, "exif-%s", exif_tag_get_title( entry->tag ) ); + vips_exif_to_s( ve->ed, entry, &value ); + + /* Can't do anything sensible with the error return. + */ + (void) im_meta_set_string( ve->image, + vips_buf_all( &name ), vips_buf_all( &value ) ); +} + +static void +attach_exif_content( ExifContent *content, VipsExif *ve ) +{ + exif_content_foreach_entry( content, + (ExifContentForeachEntryFunc) attach_exif_entry, ve ); +} + +/* Just find the first occurence of the tag (is this correct?) + */ +static ExifEntry * +find_entry( ExifData *ed, ExifTag tag ) +{ + int i; + + for( i = 0; i < EXIF_IFD_COUNT; i++ ) { + ExifEntry *entry; + + if( (entry = exif_content_get_entry( ed->ifd[i], tag )) ) + return( entry ); + } + + return( NULL ); +} + +static int +get_entry_rational( ExifData *ed, ExifTag tag, double *out ) +{ + ExifEntry *entry; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_RATIONAL || + entry->components != 1 ) + return( -1 ); + + return( vips_exif_get_double( ed, entry, 0, out ) ); +} + +static int +get_entry_short( ExifData *ed, ExifTag tag, int *out ) +{ + ExifEntry *entry; + + if( !(entry = find_entry( ed, tag )) || + entry->format != EXIF_FORMAT_SHORT || + entry->components != 1 ) + return( -1 ); + + return( vips_exif_get_int( ed, entry, 0, out ) ); +} + +static void +set_vips_resolution( IMAGE *im, ExifData *ed ) +{ + double xres, yres; + int unit; + + if( get_entry_rational( ed, EXIF_TAG_X_RESOLUTION, &xres ) || + get_entry_rational( ed, EXIF_TAG_Y_RESOLUTION, &yres ) || + get_entry_short( ed, EXIF_TAG_RESOLUTION_UNIT, &unit ) ) { + im_warn( "im_jpeg2vips", + "%s", _( "error reading resolution" ) ); + return; + } + + switch( unit ) { + case 2: + /* In inches. + */ + xres /= 25.4; + yres /= 25.4; + break; + + case 3: + /* In cm. + */ + xres /= 10.0; + yres /= 10.0; + break; + + default: + im_warn( "im_jpeg2vips", "%s", _( "bad resolution unit" ) ); + return; + } + + im->Xres = xres; + im->Yres = yres; +} + +static int +attach_thumbnail( IMAGE *im, ExifData *ed ) +{ + if( ed->size > 0 ) { + char *thumb_copy; + + thumb_copy = im_malloc( NULL, ed->size ); + memcpy( thumb_copy, ed->data, ed->size ); + + if( im_meta_set_blob( im, "jpeg-thumbnail-data", + (im_callback_fn) im_free, thumb_copy, ed->size ) ) { + im_free( thumb_copy ); + return( -1 ); + } + } + + return( 0 ); +} +#endif /*HAVE_EXIF*/ + + +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 void +vips_file_load_jpeg_class_init( VipsFileLoadJpegClass *class ) +{ + VipsObjectClass *object_class = (VipsObjectClass *) class; + + 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, + _( "Shrink" ), + _( "Shrink factor on load" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsFileLoadJpeg, shrink ), + 1, 16, 1 ); + + VIPS_ARG_BOOL( class, "fail", 6, + _( "Fail" ), + _( "Fail on first warning" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsFileLoadJpeg, fail ), + FALSE ); + +} + +static void +vips_file_load_jpeg_init( VipsFileLoadJpeg *object ) +{ + jpeg->shrink = 1; +} + + + diff --git a/libvips/format/format.c b/libvips/format/format.c index 37d8c37a..72722232 100644 --- a/libvips/format/format.c +++ b/libvips/format/format.c @@ -46,7 +46,6 @@ * * VIPS has a simple system for representing image load and save operations in * a generic way. - * * You can ask for a loader for a certain file or select a saver based on a * filename. Once you have found a format, you can use it to load a file of * that type, save an image to a file of that type, query files for their type @@ -55,7 +54,7 @@ * * If you define a new format, support for * it automatically appears in all VIPS user-interfaces. It will also be - * transparently supported by vips_image_new_from_file() and friends. + * transparently supported by im_open(). * * VIPS comes with VipsFormat for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, * Matlab, Radiance, RAW, VIPS and one that wraps libMagick. @@ -74,13 +73,14 @@ * * @VIPS_FORMAT_BIGENDIAN means that image pixels are most-significant byte * first. Depending on the native byte order of the host machine, you may - * need to swap bytes. See copy_swap(). + * need to swap bytes. See im_copy_swap(). */ /** * VipsFormat: * - * #VipsFormat has these virtual methods: + * Actually, we never make %VipsFormat objects, we just use virtual methods on + * the class object. It is defined as: * * |[ * typedef struct _VipsFormatClass { @@ -114,13 +114,12 @@ * header() This function should load the image header, * but not load any pixel data. If you don't define it, VIPS will use your * load() method instead. Return 0 for success, -1 for error, setting - * vips_error(). + * im_error(). * * * * - * load() This function should load the image, or perhaps use - * vips_image_generate() to + * load() This function should load the image, or perhaps use im_generate() to * attach something to load sections of the image on demand. * Users can embed * load options in the filename, see (for example) im_jpeg2vips(). @@ -227,9 +226,10 @@ G_DEFINE_TYPE( VipsFormatTiff, vips_format_tiff, VIPS_TYPE_FORMAT ); static void * format_add_class( VipsFormatClass *format, GSList **formats ) { - /* Append so we don't reverse the list of formats. - */ - *formats = g_slist_append( *formats, format ); + if( !G_TYPE_IS_ABSTRACT( G_OBJECT_CLASS_TYPE( format ) ) ) + /* Append so we don't reverse the list of formats. + */ + *formats = g_slist_append( *formats, format ); return( NULL ); } @@ -242,34 +242,33 @@ format_compare( VipsFormatClass *a, VipsFormatClass *b ) /** * vips_format_map: - * @base: base class to search below (eg. "VipsFormatLoad") * @fn: function to apply to each #VipsFormatClass * @a: user data * @b: user data * - * Apply a function to every #VipsFormatClass that VIPS knows about. Formats + * Apply a function to every %VipsFormatClass that VIPS knows about. Formats * are presented to the function in priority order. * * Like all VIPS map functions, if @fn returns %NULL, iteration continues. If * it returns non-%NULL, iteration terminates and that value is returned. The * map function returns %NULL if all calls return %NULL. * - * See also: vips_slist_map(). + * See also: im_slist_map(). * * Returns: the result of iteration */ void * -vips_format_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) +vips_format_map( VSListMap2Fn fn, void *a, void *b ) { GSList *formats; void *result; formats = NULL; - (void) vips_class_map_all( g_type_from_name( base ), + (void) vips_class_map_all( g_type_from_name( "VipsFormat" ), (VipsClassMapFn) format_add_class, (void *) &formats ); formats = g_slist_sort( formats, (GCompareFunc) format_compare ); - result = vips_slist_map2( formats, fn, a, b ); + result = im_slist_map2( formats, fn, a, b ); g_slist_free( formats ); return( result ); @@ -300,10 +299,16 @@ vips_format_print_class( VipsObjectClass *object_class, VipsBuf *buf ) vips_buf_appends( buf, ") " ); } - vips_buf_appends( buf, "priority=%d ", class->priority ); - 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 " ); + if( class->save ) + vips_buf_appends( buf, "save " ); + if( class->get_flags ) + vips_buf_appends( buf, "get_flags " ); } static void @@ -312,15 +317,8 @@ vips_format_class_init( VipsFormatClass *class ) VipsObjectClass *object_class = (VipsObjectClass *) class; object_class->nickname = "format"; - object_class->description = _( "file format support" ); + object_class->description = _( "VIPS file formats" ); object_class->print_class = vips_format_print_class; - - VIPS_ARG_STRING( class, "filename", 12, - _( "Filename" ), - _( "Format filename" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsFormat, filename ), - NULL ); } static void @@ -328,206 +326,232 @@ vips_format_init( VipsFormat *object ) { } -/* Abstract base class for image load. +/** + * vips_format_get_flags: + * @format: format to test + * @filename: file to test + * + * Get a set of flags for this file. + * + * Returns: flags for this format and file + */ +VipsFormatFlags +vips_format_get_flags( VipsFormatClass *format, const char *filename ) +{ + return( format->get_flags ? format->get_flags( filename ) : 0 ); +} + +/* VIPS format class. */ -G_DEFINE_ABSTRACT_TYPE( VipsFormatLoad, vips_formats_load, VIPS_TYPE_FORMAT ); +static const char *vips_suffs[] = { ".v", NULL }; -static void -vips_format_load_print_class( VipsObjectClass *object_class, VipsBuf *buf ) +int +im_isvips( const char *filename ) { - VipsFormatLoadClass *class = VIPS_FORMAT_LOAD_CLASS( object_class ); - const char **p; + unsigned char buf[4]; - VIPS_OBJECT_CLASS( vips_format_load_parent_class )-> - print_class( object_class, buf ); - vips_buf_appends( buf, ", " ); + if( im__get_bytes( filename, buf, 4 ) ) { + if( buf[0] == 0x08 && buf[1] == 0xf2 && + buf[2] == 0xa6 && buf[3] == 0xb6 ) + /* SPARC-order VIPS image. + */ + return( 1 ); + else if( buf[3] == 0x08 && buf[2] == 0xf2 && + buf[1] == 0xa6 && buf[0] == 0xb6 ) + /* INTEL-order VIPS image. + */ + return( 1 ); + } - if( class->header ) - vips_buf_appends( buf, "header " ); - if( class->load ) - vips_buf_appends( buf, "load " ); - if( class->get_flags ) - vips_buf_appends( buf, "get_flags " ); + return( 0 ); } static int -vips_format_load_build( VipsObject *object ) +file2vips( const char *filename, IMAGE *out ) { - VipsFormat *format = VIPS_FORMAT( object ); - VipsFormatLoad *load = VIPS_FORMAT_LOAD( object ); + IMAGE *im; - g_object_set( object, "out", vips_image_new(), NULL ); - - if( VIPS_OBJECT_CLASS( vips_format_load_parent_class )-> - build( object ) ) + if( !(im = im_open_local( out, filename, "r" )) || + im_copy( im, out ) ) return( -1 ); return( 0 ); } +static int +vips2file( IMAGE *im, const char *filename ) +{ + IMAGE *out; + + if( !(out = im_open_local( im, filename, "w" )) || + im_copy( im, out ) ) + return( -1 ); + + return( 0 ); +} + +static VipsFormatFlags +vips_flags( const char *filename ) +{ + VipsFormatFlags flags; + unsigned char buf[4]; + + flags = VIPS_FORMAT_PARTIAL; + + if( im__get_bytes( filename, buf, 4 ) && + buf[0] == 0x08 && + buf[1] == 0xf2 && + buf[2] == 0xa6 && + buf[3] == 0xb6 ) + flags |= VIPS_FORMAT_BIGENDIAN; + + return( flags ); +} + +/* Vips format adds no new members. + */ +typedef VipsFormat VipsFormatVips; +typedef VipsFormatClass VipsFormatVipsClass; + static void -vips_format_load_class_init( VipsFormatLoadClass *class ) +vips_format_vips_class_init( VipsFormatVipsClass *class ) { VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsFormatClass *format_class = (VipsFormatClass *) class; - object_class->nickname = "formatload"; - object_class->description = _( "format loaders" ); - object_class->print_class = vips_format_load_print_class; - object_class->build = vips_format_load_build; - - VIPS_ARG_ENUM( class, "flags", 6, - _( "Flags" ), - _( "Flags for this format" ), - VIPS_ARGUMENT_OPTIONAL_OUTPUT, - G_STRUCT_OFFSET( VipsFormatLoad, flags ), - VIPS_TYPE_FORMAT_FLAGS, VIPS_FORMAT_NONE ); - - VIPS_ARG_IMAGE( class, "out", 1, - _( "Output" ), - _( "Output image" ), - VIPS_ARGUMENT_REQUIRED_OUTPUT, - G_STRUCT_OFFSET( VipsFormatLoad, out ) ); + object_class->nickname = "vips"; + object_class->description = _( "VIPS" ); + format_class->is_a = im_isvips; + format_class->header = file2vips; + format_class->load = file2vips; + format_class->save = vips2file; + format_class->get_flags = vips_flags; + format_class->suffs = vips_suffs; } static void -vips_format_load_init( VipsFormatLoad *object ) +vips_format_vips_init( VipsFormatVips *object ) { } +G_DEFINE_TYPE( VipsFormatVips, vips_format_vips, VIPS_TYPE_FORMAT ); + +/* Called on startup: register the base vips formats. + */ +void +im__format_init( void ) +{ + extern GType vips_format_csv_get_type(); + extern GType vips_format_ppm_get_type(); + extern GType vips_format_analyze_get_type(); + extern GType vips_format_rad_get_type(); + + vips_format_vips_get_type(); +#ifdef HAVE_JPEG + extern GType vips_format_jpeg_get_type(); + vips_format_jpeg_get_type(); +#endif /*HAVE_JPEG*/ +#ifdef HAVE_PNG + extern GType vips_format_png_get_type(); + vips_format_png_get_type(); +#endif /*HAVE_PNG*/ + vips_format_csv_get_type(); + vips_format_ppm_get_type(); + vips_format_analyze_get_type(); +#ifdef HAVE_OPENEXR + extern GType vips_format_exr_get_type(); + vips_format_exr_get_type(); +#endif /*HAVE_OPENEXR*/ +#ifdef HAVE_MATIO + extern GType vips_format_mat_get_type(); + vips_format_mat_get_type(); +#endif /*HAVE_MATIO*/ +#ifdef HAVE_CFITSIO + extern GType vips_format_fits_get_type(); + vips_format_fits_get_type(); +#endif /*HAVE_CFITSIO*/ + vips_format_rad_get_type(); +#ifdef HAVE_MAGICK + extern GType vips_format_magick_get_type(); + vips_format_magick_get_type(); +#endif /*HAVE_MAGICK*/ +#ifdef HAVE_TIFF + extern GType vips_format_tiff_get_type(); + vips_format_tiff_get_type(); +#endif /*HAVE_TIFF*/ +} + /* Can this format open this file? */ static void * -vips_format_load_new_from_file_sub( VipsFormatLoadClass *load_class, - const char *filename ) +format_for_file_sub( VipsFormatClass *format, + const char *name, const char *filename ) { - if( load_class->is_a ) { - if( load_class->is_a( filename ) ) - return( load_class ); + if( format->is_a ) { + if( format->is_a( filename ) ) + return( format ); } - else if( vips_filename_suffix_match( filename, load_class->suffs ) ) - return( load_class ); + else if( im_filename_suffix_match( filename, format->suffs ) ) + return( format ); return( NULL ); } /** - * vips_format_load_new_from_file: + * vips_format_for_file: * @filename: file to find a format for * - * Searches for a format you could use to load a file. Set some more options - * after this, then call _build() to actually load in the file. + * Searches for a format you could use to load a file. * * See also: vips_format_read(), vips_format_for_name(). * * Returns: a format on success, %NULL on error */ -VipsFormatLoad * -vips_format_load_new_from_file( const char *filename ) +VipsFormatClass * +vips_format_for_file( const char *filename ) { - VipsFormatLoadClass *load_class; - VipsFormatLoad *load; + char name[FILENAME_MAX]; + char options[FILENAME_MAX]; + VipsFormatClass *format; - if( !vips_existsf( "%s", filename ) ) { - vips_error( "VipsFormatLoad", - _( "file \"%s\" not found" ), filename ); + /* Break any options off the name ... eg. "fred.tif:jpeg,tile" + * etc. + */ + im_filename_split( filename, name, options ); + + if( !im_existsf( "%s", name ) ) { + im_error( "VipsFormat", _( "file \"%s\" not found" ), name ); return( NULL ); } - if( !(load_class = (VipsFormatLoadClass *) vips_format_map( - "VipsFormatLoad", - (VipsSListMap2Fn) vips_format_load_new_from_file_sub, - (void *) filename, NULL )) ) { - vips_error( "VipsFormatLoad", + if( !(format = (VipsFormatClass *) vips_format_map( + (VSListMap2Fn) format_for_file_sub, + (void *) filename, (void *) name )) ) { + im_error( "VipsFormat", _( "file \"%s\" not a known format" ), name ); return( NULL ); } - load = VIPS_FORMAT_LOAD( - g_object_new( G_TYPE_FROM_CLASS( load_class ), NULL ) ); - - /* May as well set flags here, should be quick. - */ - if( load_class->get_flags ) - g_object_set( load, - "flags", load_class->get_flags( load ), - NULL ); - - return( load ); + return( format ); } -/* Abstract base class for image savers. - */ - -G_DEFINE_ABSTRACT_TYPE( VipsFormatSave, vips_format_save, VIPS_TYPE_FORMAT ); - -static void -vips_format_save_print_class( VipsObjectClass *object_class, VipsBuf *buf ) -{ - VipsFormatSaveClass *class = VIPS_FORMAT_SAVE_CLASS( object_class ); - const char **p; - - VIPS_OBJECT_CLASS( vips_format_save_parent_class )-> - print_class( object_class, buf ); - vips_buf_appends( buf, ", " ); - - if( class->save ) - vips_buf_appends( buf, "save " ); -} - -static int -vips_format_save_build( VipsObject *object ) -{ - VipsFormat *format = VIPS_FORMAT( object ); - VipsFormatSave *save = VIPS_FORMAT_SAVE( object ); - - if( VIPS_OBJECT_CLASS( vips_format_save_parent_class )-> - build( object ) ) - return( -1 ); - - return( 0 ); -} - -static void -vips_format_save_class_init( VipsFormatSaveClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - - object_class->nickname = "formatsave"; - object_class->description = _( "format savers" ); - object_class->print_class = vips_format_save_print_class; - object_class->build = vips_format_save_build; - - VIPS_ARG_IMAGE( class, "in", 1, - _( "Input" ), - _( "Image to save" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsFormatSave, in ) ); -} - -static void -vips_format_save_init( VipsFormat *object ) -{ -} - -/* Can we write this filename with this format? +/* Can we write this filename with this format? Ignore formats without a save + * method. */ static void * -vips_format_save_new_from_filename_sub( VipsFormatSaveClass *save_class, - const char *filename ) +format_for_name_sub( VipsFormatClass *format, const char *name ) { - VipsFormatClass *format = VIPS_FORMAT_CLASS( save_class ); - - if( save_class->save && - vips_filename_suffix_match( filename, format->suffs ) ) - return( save_class ); + if( format->save && + im_filename_suffix_match( name, format->suffs ) ) + return( format ); return( NULL ); } /** - * vips_format_save_new_from_filename: + * vips_format_for_name: * @filename: name to find a format for * * Searches for a format you could use to save a file. @@ -536,27 +560,22 @@ vips_format_save_new_from_filename_sub( VipsFormatSaveClass *save_class, * * Returns: a format on success, %NULL on error */ -VipsFormatSave * -vips_format_save_new_from_filename( const char *filename ) +VipsFormatClass * +vips_format_for_name( const char *filename ) { - VipsFormatSaveClass *save_class; - VipsFormatSave *save; + VipsFormatClass *format; - if( !(save_class = (VipsFormatSaveClass *) vips_format_map( - "VipsFormatSave", - (VipsSListMap2Fn) vips_format_save_new_from_filename_sub, + if( !(format = (VipsFormatClass *) vips_format_map( + (VSListMap2Fn) format_for_name_sub, (void *) filename, NULL )) ) { - vips_error( "VipsFormatSave", + im_error( "VipsFormat", _( "\"%s\" is not a supported image format." ), filename ); return( NULL ); } - save = VIPS_FORMAT_SAVE( - g_object_new( G_TYPE_FROM_CLASS( save_class ), NULL ) ); - - return( save ); + return( format ); } /** @@ -571,13 +590,13 @@ vips_format_save_new_from_filename( const char *filename ) * Returns: 0 on success, -1 on error */ int -vips_format_read( const char *filename, VipsImage *out ) +vips_format_read( const char *filename, IMAGE *out ) { - VipsFormatLoad *load; + VipsFormatClass *format; - if( !(load = vips_format_load_new_from_file( filename )) ) + if( !(format = vips_format_for_file( filename )) || + format->load( filename, out ) ) return( -1 ); - g_object_unref( load ); return( 0 ); } @@ -604,4 +623,3 @@ vips_format_write( IMAGE *in, const char *filename ) return( 0 ); } - diff --git a/libvips/include/vips/Makefile.am b/libvips/include/vips/Makefile.am index 44a7ec02..8ff58f75 100644 --- a/libvips/include/vips/Makefile.am +++ b/libvips/include/vips/Makefile.am @@ -57,6 +57,7 @@ EXTRA_DIST = version.h.in internal.h enumtemplate # well vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/memory.h \ + ${top_srcdir}/libvips/include/vips/file.h \ ${top_srcdir}/libvips/include/vips/arithmetic.h \ ${top_srcdir}/libvips/include/vips/conversion.h \ ${top_srcdir}/libvips/include/vips/util.h \ diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 0c8bc213..d1718864 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -6,6 +6,9 @@ G_BEGIN_DECLS +/* enumerations from "../../../libvips/include/vips/file.h" */ +GType vips_file_flags_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_FILE_FLAGS (vips_file_flags_get_type()) /* enumerations from "../../../libvips/include/vips/arithmetic.h" */ GType vips_operation_math_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_OPERATION_MATH (vips_operation_math_get_type()) diff --git a/libvips/include/vips/file.h b/libvips/include/vips/file.h index 2ec18589..dd0211b3 100644 --- a/libvips/include/vips/file.h +++ b/libvips/include/vips/file.h @@ -28,8 +28,8 @@ */ -#ifndef IM_FILE_H -#define IM_FILE_H +#ifndef VIPS_FILE_H +#define VIPS_FILE_H #ifdef __cplusplus extern "C" { @@ -64,13 +64,6 @@ typedef struct _VipsFileClass { VipsOperationClass parent_class; /*< public >*/ - /* Is a file in this file. - */ - gboolean (*is_a)( const char * ); - - /* Null-terminated list of allowed suffixes, eg. ".tif", ".tiff". - */ - const char **suffs; /* Loop over files in this order, default 0. We need this because * some files can be read by several loaders (eg. tiff can be read @@ -79,6 +72,11 @@ 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. + */ + const char **suffs; + } VipsFileClass; GType vips_file_get_type( void ); @@ -86,7 +84,7 @@ GType vips_file_get_type( void ); /* Map over and find files. This uses type introspection to loop over * subclasses of VipsFile. */ -void *vips_file_map( VipsSListMap2Fn fn, void *a, void *b ); +void *vips_file_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ); #define VIPS_TYPE_FILE_LOAD (vips_file_load_get_type()) #define VIPS_FILE_LOAD( obj ) \ @@ -115,14 +113,22 @@ typedef struct _VipsFileLoad { VipsFile parent_object; /*< public >*/ + /* Open to memory (default is to open via disc). + */ + gboolean memory; + /* Flags read from the file. */ VipsFileFlags flags; - /* The image we've loaded. + /* The image we generate. */ VipsImage *out; + /* The behind-the-scenes real image we decompress to. + */ + VipsImage *real; + } VipsFileLoad; typedef struct _VipsFileLoadClass { @@ -130,17 +136,27 @@ typedef struct _VipsFileLoadClass { /*< public >*/ + /* Is a file in this format. + */ + gboolean (*is_a)( const char * ); + /* Get the flags for this file in this file. */ VipsFileFlags (*get_flags)( VipsFileLoad * ); + /* Read the header into @out. + */ + int (*header)( VipsFileLoad * ); + + /* Read the whole image into @out. + */ + int (*load)( VipsFileLoad * ); + } VipsFileLoadClass; GType vips_file_load_get_type( void ); -VipsFileLoad *vips_file_load_new_from_file( const char *filename ); - -VipsFileLoad *vips_file_for_file( const char *filename ); +const char *vips_file_find_load( const char *filename ); #define VIPS_TYPE_FILE_SAVE (vips_file_save_get_type()) #define VIPS_FILE_SAVE( obj ) \ @@ -176,21 +192,15 @@ typedef struct _VipsFileSaveClass { GType vips_file_save_get_type( void ); -VipsFileSave *vips_file_save_new_from_filename( const char *filename ) +const char *vips_file_find_save( const char *filename ); /* Read/write an image convenience functions. */ -int vips_file_read( const char *filename, VipsImage *out ); -int vips_file_write( VipsImage *in, const char *filename ); - - - - - - +int vips_file_read( const char *filename, VipsImage **out, ... ); +int vips_file_write( VipsImage *in, const char *filename, ... ); #ifdef __cplusplus } #endif /*__cplusplus*/ -#endif /*IM_FILE_H*/ +#endif /*VIPS_FILE_H*/ diff --git a/libvips/include/vips/format.h b/libvips/include/vips/format.h index 8b902d39..9153293a 100644 --- a/libvips/include/vips/format.h +++ b/libvips/include/vips/format.h @@ -50,14 +50,21 @@ extern "C" { (G_TYPE_INSTANCE_GET_CLASS( (obj), \ VIPS_TYPE_FORMAT, VipsFormatClass )) +/* Image file properties. + */ +typedef enum { + VIPS_FORMAT_NONE = 0, /* No flags set */ + VIPS_FORMAT_PARTIAL = 1, /* Lazy read OK (eg. tiled tiff) */ + VIPS_FORMAT_BIGENDIAN = 2 /* Most-significant byte first */ +} VipsFormatFlags; + +/* Don't instantiate these things, just use the class stuff. + */ + typedef struct _VipsFormat { VipsObject parent_object; /*< public >*/ - /* Filename for load or save. - */ - char *filename; - } VipsFormat; typedef struct _VipsFormatClass { @@ -68,9 +75,21 @@ typedef struct _VipsFormatClass { */ gboolean (*is_a)( const char * ); - /* Null-terminated list of allowed suffixes, eg. ".tif", ".tiff". + /* Read just the header into the VipsImage. */ - const char **suffs; + int (*header)( const char *, VipsImage * ); + + /* Load the whole image. + */ + int (*load)( const char *, VipsImage * ); + + /* Write the VipsImage to the file in this format. + */ + int (*save)( VipsImage *, const char * ); + + /* Get the flags for this file in this format. + */ + VipsFormatFlags (*get_flags)( const char * ); /* Loop over formats in this order, default 0. We need this because * some formats can be read by several loaders (eg. tiff can be read @@ -79,6 +98,9 @@ typedef struct _VipsFormatClass { */ int priority; + /* Null-terminated list of allowed suffixes, eg. ".tif", ".tiff". + */ + const char **suffs; } VipsFormatClass; GType vips_format_get_type( void ); @@ -87,108 +109,17 @@ GType vips_format_get_type( void ); * subclasses of VipsFormat. */ void *vips_format_map( VipsSListMap2Fn fn, void *a, void *b ); +VipsFormatClass *vips_format_for_file( const char *filename ); +VipsFormatClass *vips_format_for_name( const char *filename ); -#define VIPS_TYPE_FORMAT_LOAD (vips_format_load_get_type()) -#define VIPS_FORMAT_LOAD( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_FORMAT_LOAD, VipsFormatLoad )) -#define VIPS_FORMAT_LOAD_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_FORMAT_LOAD, VipsFormatLoadClass)) -#define VIPS_IS_FORMAT_LOAD( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FORMAT_LOAD )) -#define VIPS_IS_FORMAT_LOAD_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FORMAT_LOAD )) -#define VIPS_FORMAT_LOAD_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_FORMAT_LOAD, VipsFormatLoadClass )) - -/* Image file properties. - */ -typedef enum { - VIPS_FORMAT_NONE = 0, /* No flags set */ - VIPS_FORMAT_PARTIAL = 1, /* Lazy read OK (eg. tiled tiff) */ - VIPS_FORMAT_BIGENDIAN = 2 /* Most-significant byte first */ -} VipsFormatFlags; - -typedef struct _VipsFormatLoad { - VipsFormat parent_object; - /*< public >*/ - - /* Flags read from the file. - */ - VipsFormatFlags flags; - - /* The image we've loaded. - */ - VipsImage *out; - -} VipsFormatLoad; - -typedef struct _VipsFormatLoadClass { - VipsFormatClass parent_class; - - /*< public >*/ - - /* Get the flags for this file in this format. - */ - VipsFormatFlags (*get_flags)( VipsFormatLoad * ); - -} VipsFormatLoadClass; - -GType vips_format_load_get_type( void ); - -VipsFormatLoad *vips_format_load_new_from_file( const char *filename ); - -VipsFormatLoad *vips_format_for_file( const char *filename ); - -#define VIPS_TYPE_FORMAT_SAVE (vips_format_save_get_type()) -#define VIPS_FORMAT_SAVE( obj ) \ - (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ - VIPS_TYPE_FORMAT_SAVE, VipsFormatSave )) -#define VIPS_FORMAT_SAVE_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_CAST( (klass), \ - VIPS_TYPE_FORMAT_SAVE, VipsFormatSaveClass)) -#define VIPS_IS_FORMAT_SAVE( obj ) \ - (G_TYPE_CHECK_INSTANCE_TYPE( (obj), VIPS_TYPE_FORMAT_SAVE )) -#define VIPS_IS_FORMAT_SAVE_CLASS( klass ) \ - (G_TYPE_CHECK_CLASS_TYPE( (klass), VIPS_TYPE_FORMAT_SAVE )) -#define VIPS_FORMAT_SAVE_GET_CLASS( obj ) \ - (G_TYPE_INSTANCE_GET_CLASS( (obj), \ - VIPS_TYPE_FORMAT_SAVE, VipsFormatSaveClass )) - -typedef struct _VipsFormatSave { - VipsFormat parent_object; - /*< public >*/ - - /* The image we are to save. - */ - VipsImage *in; - -} VipsFormatSave; - -typedef struct _VipsFormatSaveClass { - VipsFormatClass parent_class; - - /*< public >*/ - -} VipsFormatSaveClass; - -GType vips_format_save_get_type( void ); - -VipsFormatSave *vips_format_save_new_from_filename( const char *filename ) +VipsFormatFlags vips_format_get_flags( VipsFormatClass *format, + const char *filename ); /* Read/write an image convenience functions. */ int vips_format_read( const char *filename, VipsImage *out ); int vips_format_write( VipsImage *in, const char *filename ); - - - - - - /* Low-level read/write operations. */ int im_jpeg2vips( const char *filename, VipsImage *out ); diff --git a/libvips/include/vips/vips.h b/libvips/include/vips/vips.h index 4a17d33b..f8833eb8 100644 --- a/libvips/include/vips/vips.h +++ b/libvips/include/vips/vips.h @@ -108,7 +108,6 @@ extern "C" { #include #include #include -#include #include #include #include @@ -117,6 +116,7 @@ extern "C" { #include #include #include +#include #include diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index 2a5f4b77..784f936b 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -41,6 +41,7 @@ INCLUDES = -I${top_srcdir}/libvips/include @VIPS_CFLAGS@ @VIPS_INCLUDES@ # well vips_scan_headers = \ ${top_srcdir}/libvips/include/vips/memory.h \ + ${top_srcdir}/libvips/include/vips/file.h \ ${top_srcdir}/libvips/include/vips/conversion.h \ ${top_srcdir}/libvips/include/vips/arithmetic.h \ ${top_srcdir}/libvips/include/vips/util.h \ diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index ff07ab6a..1a8df7e5 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -4,6 +4,25 @@ /* auto-generated enums for vips introspection */ #include +/* enumerations from "../../libvips/include/vips/file.h" */ +GType +vips_file_flags_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_FILE_NONE, "VIPS_FILE_NONE", "none"}, + {VIPS_FILE_PARTIAL, "VIPS_FILE_PARTIAL", "partial"}, + {VIPS_FILE_BIGENDIAN, "VIPS_FILE_BIGENDIAN", "bigendian"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsFileFlags", values ); + } + + return( etype ); +} /* enumerations from "../../libvips/include/vips/conversion.h" */ GType vips_extend_get_type( void )