From 62836151d4feaf4cec363c8980d425786c6cb8b5 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 15 Feb 2011 14:41:46 +0000 Subject: [PATCH] more vipsimage hacking Moved all of im_close.c and im_open.c into the image.c class file, various small cleanups. --- TODO | 2 +- libvips/include/vips/image.h | 19 +- libvips/include/vips/object.h | 2 + libvips/iofuncs/Makefile.am | 3 - libvips/iofuncs/im_close.c | 319 ------------- libvips/iofuncs/im_open.c | 709 ---------------------------- libvips/iofuncs/im_unmapfile.c | 71 --- libvips/iofuncs/image.c | 834 +++++++++++++++++++++++++++++---- libvips/iofuncs/object.c | 40 ++ 9 files changed, 803 insertions(+), 1196 deletions(-) delete mode 100644 libvips/iofuncs/im_close.c delete mode 100644 libvips/iofuncs/im_open.c delete mode 100644 libvips/iofuncs/im_unmapfile.c diff --git a/TODO b/TODO index b729e11e..2dd3f4ce 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,5 @@ -- _build() for "t" / "p" / "w" sould do setupout / generate / etc.? +- image.c has far too many includes diff --git a/libvips/include/vips/image.h b/libvips/include/vips/image.h index 9385b8eb..43413f09 100644 --- a/libvips/include/vips/image.h +++ b/libvips/include/vips/image.h @@ -241,6 +241,11 @@ typedef struct _VipsImage { */ gboolean hint_set; + /* The pre/post/close callbacks are all fire-once. + */ + gboolean preclose; + gboolean close; + gboolean postclose; } VipsImage; typedef struct _VipsImageClass { @@ -251,33 +256,33 @@ typedef struct _VipsImageClass { /* Evaluation is starting. */ - int (*evalstart)( VipsImage *image ); + void (*preeval)( VipsImage *image ); /* Evaluation progress. */ - int (*eval)( VipsImage *image, VipsProgress *progress ); + void (*eval)( VipsImage *image, VipsProgress *progress ); /* Evaluation is ending. */ - int (*evalend)( VipsImage *image ); + void (*posteval)( VipsImage *image ); /* Just before image close, everything is still alive. */ - int (*preclose)( VipsImage *image ); + void (*preclose)( VipsImage *image ); /* Image close, time to free stuff. */ - int (*preclose)( VipsImage *image ); + void (*close)( VipsImage *image ); /* Post-close, everything is dead, except the VipsImage pointer. * Useful for eg. deleting the file associated with a temp image. */ - int (*postclose)( VipsImage *image ); + void (*postclose)( VipsImage *image ); /* An image has been written to. * Used by eg. im_open("x.jpg", "w") to do the final write to jpeg. */ - int (*written)( VipsImage *image ); + void (*written)( VipsImage *image ); /* An image has been modified in some way and downstream caches all * need dropping. diff --git a/libvips/include/vips/object.h b/libvips/include/vips/object.h index 6bef6c4d..83dbc297 100644 --- a/libvips/include/vips/object.h +++ b/libvips/include/vips/object.h @@ -241,6 +241,8 @@ VipsObject *vips_object_new( GType type, VipsObject *vips_object_new_from_string( const char *base, const char *str ); void vips_object_to_string( VipsObject *object, VipsBuf *buf ); +void *vips_object_map( VSListMap2Fn fn, void *a, void *b ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/Makefile.am b/libvips/iofuncs/Makefile.am index a6a2f53c..78542a56 100644 --- a/libvips/iofuncs/Makefile.am +++ b/libvips/iofuncs/Makefile.am @@ -13,19 +13,16 @@ libiofuncs_la_SOURCES = \ image.c \ vips.c \ im_binfile.c \ - im_close.c \ im_cp_desc.c \ im_demand_hint.c \ im_generate.c \ im_histlin.c \ im_image.c \ im_mapfile.c \ - im_open.c \ im_partial.c \ im_prepare.c \ im_setbuf.c \ im_setupout.c \ - im_unmapfile.c \ im_guess_prefix.c \ sinkmemory.c \ sinkscreen.c \ diff --git a/libvips/iofuncs/im_close.c b/libvips/iofuncs/im_close.c deleted file mode 100644 index ab6b5269..00000000 --- a/libvips/iofuncs/im_close.c +++ /dev/null @@ -1,319 +0,0 @@ -/* im_close.c --- close an image - * - * Copyright: Nicos Dessipris - * Written on: 12/04/1990 - * Modified on : - * 24/7/92 JC - * - im_update_descfile code tidied up - * - free on NULL string when junking Hist fixed - * - now calls im_unmapfile - * - better behaviour if image has been opened and closed with - * no im_setupout call - * - better behaviour for half-made IMAGE descriptors - * 15/4/93 JC - * - additions for freeing partial images - * 29/4/93 JC - * - close callback list added - * 10/5/93 JC - * - im__close() added - * 9/11/93 JC - * - im_update_descfile -> write_descfile - * - if Hist is NULL, no longer makes up and writes .desc file - * 16/8/94 JC - * - evalend callbacks added - * - ANSIfied - * 24/10/95 JC - * - now tracks open images ... see also im_init() and debug.c - * 11/7/00 JC - * - SETBUF_FOREIGN added - * 16/1/04 JC - * - frees as much as possible on im_close() failure - * 6/6/05 Markus Wollgarten - * - free Meta on close - * 30/6/05 JC - * - actually, free Meta on final close, so we carry meta over on an - * im__close()/im_openin() pair (eg. see im_pincheck()) - * 11/7/05 - * - call im__writehist() to send history to XML after image data - * 3/1/07 - * - free history_list - * 7/11/07 - * - added preclose, removed evalend triggers - * 23/7/08 - * - im__close() will no longer free regions - * 9/8/08 - * - lock global image list (thanks Lee) - * 8/9/09 - * - move close callbacks *after* we have released resources --- we - * can now write close callbacks that unlink() temporary files - * - use preclose callbacks if you want to run before resources are - * released - * 6/10/09 - * - gtkdoc comment - * 10/1/09 - * - added postclose - * 14/1/09 - * - added written - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* -#define DEBUG_IO -#define DEBUG_NEW - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif /*HAVE_UNISTD_H*/ -#ifdef HAVE_IO_H -#include -#endif /*HAVE_IO_H*/ -#include - -#include -#include -#include -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -/* Maximum file name length. - */ -#define NAMELEN 1024 - -/* Free any resources owned by this descriptor. The descriptor is left as if a - * call to im_init had just happened - ie. the filename is set, but no other - * resources are attached. Information is lost if this is a im_setbuf() - * image! On an error, return non-zero and leave the image in an indeterminate - * state. Too hard to recover gracefully. - */ -int -im__close( IMAGE *im ) -{ - int result; - - result = 0; - - /* No action for NULL image. - */ - if( !im ) - return( result ); - -#ifdef DEBUG_IO - printf( "im__close: starting for %s ..\n", im->filename ); -#endif /*DEBUG_IO*/ - - /* Trigger all pre-close fns. - */ - result |= im__trigger_callbacks( im->preclosefns ); - IM_FREEF( im_slist_free_all, im->preclosefns ); - - /* Should be no regions defined on the image. im_close() ought to put - * us into a zombie state if there are, im__close() should not be - * called on images with running regions. - */ - if( im->regions ) { - GSList *p; - - printf( "** im__close: leaked regions!\n" ); - for( p = im->regions; p; p = p->next ) - im_region_print( (REGION *) p->data ); - } - - /* That should mean we have no windows. - */ - if( im->windows ) { - GSList *p; - - printf( "** im__close: leaked windows!\n" ); - for( p = im->windows; p; p = p->next ) - im_window_print( (im_window_t *) p->data ); - } - - /* Junk generate functions. - */ - im->start = NULL; - im->generate = NULL; - im->stop = NULL; - - /* No more upstream/downstream links. - */ - im__link_break_all( im ); - - /* What resources are associated with this IMAGE descriptor? - */ - if( im->baseaddr ) { - /* MMAP file. - */ -#ifdef DEBUG_IO - printf( "im__close: unmapping file ..\n" ); -#endif /*DEBUG_IO*/ - - if( im_unmapfile( im ) ) - return( -1 ); - im->data = NULL; - } - - /* Is there a file descriptor? - */ - if( im->fd != -1 ) { -#ifdef DEBUG_IO - printf( "im__close: closing output file ..\n" ); -#endif /*DEBUG_IO*/ - - if( im->dtype == IM_OPENOUT && im__writehist( im ) ) - result = -1; - if( close( im->fd ) == -1 ) { - im_error( "im_close", _( "unable to close fd for %s" ), - im->filename ); - result = -1; - } - im->fd = -1; - } - - /* Any image data? - */ - if( im->data ) { - /* Buffer image. Only free stuff we know we allocated. - */ - if( im->dtype == IM_SETBUF ) { -#ifdef DEBUG_IO - printf( "im__close: freeing buffer ..\n" ); -#endif /*DEBUG_IO*/ - im_free( im->data ); - im->dtype = IM_NONE; - } - - im->data = NULL; - } - - /* Junk all callbacks, perform close callbacks. - */ - IM_FREEF( im_slist_free_all, im->evalstartfns ); - IM_FREEF( im_slist_free_all, im->evalfns ); - IM_FREEF( im_slist_free_all, im->evalendfns ); - IM_FREEF( im_slist_free_all, im->invalidatefns ); - IM_FREEF( im_slist_free_all, im->writtenfns ); - result |= im__trigger_callbacks( im->closefns ); - IM_FREEF( im_slist_free_all, im->closefns ); - - /* Reset other state. - */ - im->dtype = IM_NONE; - im->dhint = IM_SMALLTILE; - im->kill = 0; - im->close_pending = 0; - im->sizeof_header = IM_SIZEOF_HEADER; - -#ifdef DEBUG_IO - printf( "im__close: final success for %s (%p)\n", - im->filename, im ); -#endif /*DEBUG_IO*/ - - return( result ); -} - -/** - * im_close: - * @im: image to close - * - * Frees all resources associated with @im. - * - * If there are open #REGION s on @im, close is delayed until the last region - * is freed. - * - * See also: im_open(). - * - * Returns: 0 on success and 1 on error. - */ -int -im_close( IMAGE *im ) -{ - int result = 0; - - /* No action for NULL image. - */ - if( !im ) - return( result ); - - if( im->regions ) { - /* There are regions left on this image. - * Set close_pending and return. The image will be then - * be closed when the last region is freed - * (see im_region_free()). - */ -#ifdef DEBUG_NEW - printf( "im_close: pending close for 0x%p, \"%s\"\n", - im, im->filename ); -#endif /*DEBUG_NEW*/ - - im->close_pending = 1; - } - else if( !im->closing ) { - /* Is this descriptor currently being closed somewhere else? - * This prevents infinite descent if a close callback - * includes an im_close for this image. - */ - im->closing = 1; - - if( im__close( im ) ) - result = -1; - -#ifdef DEBUG_NEW - printf( "im_close: freeing IMAGE 0x%p, \"%s\"\n", - im, im->filename ); -#endif /*DEBUG_NEW*/ - - /* Final cleanup. - */ - result |= im__trigger_callbacks( im->postclosefns ); - IM_FREEF( im_slist_free_all, im->postclosefns ); - IM_FREEF( g_mutex_free, im->sslock ); - IM_FREE( im->filename ); - IM_FREE( im->Hist ); - IM_FREEF( im__gslist_gvalue_free, im->history_list ); - im__meta_destroy( im ); - g_mutex_lock( im__global_lock ); - im__open_images = g_slist_remove( im__open_images, im ); - g_mutex_unlock( im__global_lock ); - im__time_destroy( im ); - IM_FREE( im ); - } - - return( result ); -} diff --git a/libvips/iofuncs/im_open.c b/libvips/iofuncs/im_open.c deleted file mode 100644 index 786df366..00000000 --- a/libvips/iofuncs/im_open.c +++ /dev/null @@ -1,709 +0,0 @@ -/* @(#) im_open: vips front end to file open functions -ARGS: filename and mode which is one of: -"r" read by mmap (im_mmapin) -"rw" read and write by mmap (im_mmapinrw) -"w" write for write (im_writeline) -"t" for temporary memory image -"p" for partial memory image -RETURNS: IMAGE pointer or 0 on error -Copyright: Kirk Martinez 29/5/92 - -Modified: - * 8/8/94 JC - * - ANSIfied - * - im_open_local added - * 16/8/94 JC - * - im_malloc() added - * 22/11/94 JC & KM - * - tiff added - * 1/2/95 JC - * - tiff removed again! - * - now just im_istiff(), im_tiff2vips() and im_vips2tiff() - * - applications have responsibility for making the translation - * 26/3/96 JC - * - im_open_local() now closes on close, not evalend - * 14/11/96 JC - * - tiff and jpeg added again - * - open for read used im_istiff() and im_isjpeg() to switch - * - open for write looks at suffix - * 23/4/97 JC - * - im_strdup() now allows NULL IMAGE parameter - * - subsample parameter added to im_tiff2vips() - * 29/10/98 JC - * - byte-swap stuff added - * 16/6/99 JC - * - 8x byte swap added for double/double complex - * - better error message if file does not exist - * - ignore case when testing suffix for save - * - fix im_mmapinrw() return test to stop coredump in edvips if - * unwritable - * 2/11/99 JC - * - malloc/strdup stuff moved to memory.c - * 5/8/00 JC - * - fixes for im_vips2tiff() changes - * 13/10/00 JC - * - ooops, missing 'else' - * 22/11/00 JC - * - ppm read/write added - * 12/2/01 JC - * - im__image_sanity() added - * 16/5/01 JC - * - now allows RW for non-native byte order, provided it's an 8-bit - * image - * 11/7/01 JC - * - im_tiff2vips() no longer has subsample option - * 25/3/02 JC - * - better im_open() error message - * 12/12/02 JC - * - sanity no longer returns errors, just warns - * 28/12/02 HB - * - Added PNG support - * 6/5/03 JC - * - added im_open_header() (from header.c) - * 22/5/03 JC - * - im__skip_dir() knows about ':' in filenames for vips2tiff etc. - * 27/11/03 JC - * - im_image_sanity() now insists x/y/bands all >0 - * 6/1/04 JC - * - moved util funcs out to util.c - * 18/2/04 JC - * - added an im_init_world() to help old progs - * 16/4/04 - * - oop, im_open() didn't know about input options in filenames - * 2/11/04 - * - im_open( "r" ) is now lazier ... for non-VIPS formats, we delay - * read until the first ->generate() - * - so im_open_header() is now a no-op - * 22/6/05 - * - if TIFF open fails, try going through libmagick - * 4/8/05 - * - added analyze read - * 30/9/05 - * - oops, lazy read error recovery didn't clear a pointer - * 1/5/06 - * - added OpenEXR read - * 9/6/06 - * - added CSV read/write - * 20/9/06 - * - test for NULL filename/mode, common if you forget to check argc - * (thanks bruno) - * 7/11/07 - * - use preclose, not evalend, for delayed save - * - add simple cmd-line progress feedback - * 9/8/08 - * - lock global image list (thanks lee) - * 25/5/08 - * - break file format stuff out to the new pluggable image format system - * 14/1/09 - * - write to non-vips formats with a "written" callback - * 29/7/10 - * - disc open threshold stuff, open to disc mode - * 27/10/10 - * - oops, guess_size was unnecessary - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -/* -#define DEBUG - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#include -#include -#include - -#include -#include -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -/* Progress feedback. Only really useful for testing, tbh. - */ -int im__progress = 0; - -/* A string giving the image size (in bytes of uncompressed image) above which - * we decompress to disc on open. Can be eg. "12m" for 12 megabytes. - */ -char *im__disc_threshold = NULL; - -/* Delayed save: if we write to (eg.) TIFF, actually do the write - * to a "p" and on "written" do im_vips2tiff() or whatever. Track save - * parameters here. - */ -typedef struct { - int (*save_fn)(); /* Save function */ - IMAGE *im; /* Image to save */ - char *filename; /* Save args */ -} SaveBlock; - -/* From "written" callback: invoke a delayed save. - */ -static int -invoke_sb( SaveBlock *sb ) -{ - if( sb->save_fn( sb->im, sb->filename ) ) - return( -1 ); - - return( 0 ); -} - -/* Attach a SaveBlock to an image. - */ -static int -attach_sb( IMAGE *out, int (*save_fn)(), const char *filename ) -{ - SaveBlock *sb = IM_NEW( out, SaveBlock ); - - if( !sb ) - return( -1 ); - sb->im = out; - sb->save_fn = save_fn; - sb->filename = im_strdup( out, filename ); - - if( im_add_written_callback( out, - (im_callback_fn) invoke_sb, (void *) sb, NULL ) ) - return( -1 ); - - return( 0 ); -} - -/* Lazy open: init a descriptor from a filename (just read the header) but - * delay actually decoding pixels until the first call to a start function. - */ - -/* What we track during a delayed open. - */ -typedef struct _Lazy { - IMAGE *out; - VipsFormatClass *format;/* Read in pixels with this */ - gboolean disc; /* Read via disc requested */ - IMAGE *im; /* The real decompressed image */ -} Lazy; - -static int -lazy_free( Lazy *lazy ) -{ - IM_FREEF( im_close, lazy->im ); - - return( 0 ); -} - -static Lazy * -lazy_new( IMAGE *out, VipsFormatClass *format, gboolean disc ) -{ - Lazy *lazy; - - if( !(lazy = IM_NEW( out, Lazy )) ) - return( NULL ); - lazy->out = out; - lazy->format = format; - lazy->disc = disc; - lazy->im = NULL; - - if( im_add_close_callback( out, - (im_callback_fn) lazy_free, lazy, NULL ) ) { - lazy_free( lazy ); - - return( NULL ); - } - - return( lazy ); -} - -typedef struct { - const char unit; - int multiplier; -} Unit; - -static size_t -parse_size( const char *size_string ) -{ - static Unit units[] = { - { 'k', 1024 }, - { 'm', 1024 * 1024 }, - { 'g', 1024 * 1024 * 1024 } - }; - - size_t size; - int n; - int i, j; - char *unit; - - /* An easy way to alloc a buffer large enough. - */ - unit = g_strdup( size_string ); - n = sscanf( size_string, "%d %s", &i, unit ); - if( n > 0 ) - size = i; - if( n > 1 ) { - for( j = 0; j < IM_NUMBER( units ); j++ ) - if( tolower( unit[0] ) == units[j].unit ) { - size *= units[j].multiplier; - break; - } - } - g_free( unit ); - -#ifdef DEBUG - printf( "parse_size: parsed \"%s\" as %zd\n", size_string, size ); -#endif /*DEBUG*/ - - return( size ); -} - -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 = parse_size( env ); - - if( im__disc_threshold ) - threshold = parse_size( im__disc_threshold ); - -#ifdef DEBUG - printf( "disc_threshold: %zd bytes\n", threshold ); -#endif /*DEBUG*/ - } - - return( threshold ); -} - -static IMAGE * -lazy_image( Lazy *lazy ) -{ - IMAGE *im; - - /* We open to disc if: - * - 'disc' is set - * - disc_threshold() has not been set to zero - * - the format does not support lazy read - * - the image will be more than a megabyte, uncompressed - */ - im = NULL; - if( lazy->disc && - disc_threshold() && - !(vips_format_get_flags( lazy->format, lazy->out->filename ) & - VIPS_FORMAT_PARTIAL) ) { - size_t size = IM_IMAGE_SIZEOF_LINE( lazy->out ) * - lazy->out->Ysize; - - if( size > disc_threshold() ) { - if( !(im = im__open_temp( "%s.v" )) ) - return( NULL ); - -#ifdef DEBUG - printf( "lazy_image: opening to disc file \"%s\"\n", - im->filename ); -#endif /*DEBUG*/ - } - } - - /* Otherwise, fall back to a "p". - */ - if( !im && - !(im = im_open( lazy->out->filename, "p" )) ) - return( NULL ); - - return( im ); -} - -/* Our start function ... do the lazy open, if necessary, and return a region - * on the new image. - */ -static void * -open_lazy_start( IMAGE *out, void *a, void *dummy ) -{ - Lazy *lazy = (Lazy *) a; - - if( !lazy->im ) { - if( !(lazy->im = lazy_image( lazy )) || - lazy->format->load( lazy->out->filename, lazy->im ) || - im_pincheck( lazy->im ) ) { - IM_FREEF( im_close, lazy->im ); - return( NULL ); - } - } - - return( im_region_create( lazy->im ) ); -} - -/* Just copy. - */ -static int -open_lazy_generate( REGION *or, void *seq, void *a, void *b ) -{ - REGION *ir = (REGION *) seq; - - Rect *r = &or->valid; - - /* Ask for input we need. - */ - if( im_prepare( ir, r ) ) - return( -1 ); - - /* Attach output region to that. - */ - if( im_region_region( or, ir, r, r->left, r->top ) ) - return( -1 ); - - return( 0 ); -} - -/* Lazy open ... init the header with the first OpenLazyFn, delay actually - * decoding pixels with the second OpenLazyFn until the first generate(). - */ -static int -open_lazy( VipsFormatClass *format, gboolean disc, IMAGE *out ) -{ - Lazy *lazy; - - if( !(lazy = lazy_new( out, format, disc )) ) - 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( format->header( out->filename, out ) || - im_demand_hint( out, IM_THINSTRIP, NULL ) ) - return( -1 ); - - /* Then 'start' creates the real image and 'gen' paints 'out' with - * pixels from the real image on demand. - */ - if( im_generate( out, - open_lazy_start, open_lazy_generate, im_stop_one, - lazy, NULL ) ) - return( -1 ); - - return( 0 ); -} - -static IMAGE * -open_sub( VipsFormatClass *format, const char *filename, gboolean disc ) -{ - IMAGE *im; - - /* This is the 'im' we return which, when read from, will trigger the - * actual load. - */ - if( !(im = im_open( filename, "p" )) || - open_lazy( format, disc, im ) ) { - im_close( im ); - return( NULL ); - } - - return( im ); -} - -/* Progress feedback. - */ - -/* What we track during an eval. - */ -typedef struct { - IMAGE *im; - - int last_percent; /* The last %complete we displayed */ -} Progress; - -int -evalstart_cb( Progress *progress ) -{ - int tile_width; - int tile_height; - int nlines; - - progress->last_percent = 0; - - vips_get_tile_size( progress->im, &tile_width, &tile_height, &nlines ); - printf( _( "%s %s: %d threads, %d x %d tiles, groups of %d scanlines" ), - g_get_prgname(), progress->im->filename, - im_concurrency_get(), - tile_width, tile_height, nlines ); - printf( "\n" ); - - return( 0 ); -} - -int -eval_cb( Progress *progress ) -{ - IMAGE *im = progress->im; - - if( im->time->percent != progress->last_percent ) { - printf( _( "%s %s: %d%% complete" ), - g_get_prgname(), im->filename, im->time->percent ); - printf( "\r" ); - fflush( stdout ); - - progress->last_percent = im->time->percent; - } - - return( 0 ); -} - -int -evalend_cb( Progress *progress ) -{ - IMAGE *im = progress->im; - - /* Spaces at end help to erase the %complete message we overwrite. - */ - printf( _( "%s %s: done in %ds \n" ), - g_get_prgname(), im->filename, im->time->run ); - - return( 0 ); -} - -/** - * im_open: - * @filename: file to open - * @mode: mode to open with - * - * im_open() examines the mode string, and creates an appropriate #IMAGE. - * - * - * - * - * "r" - * opens the named file for reading. If the file is not in the native - * VIPS format for your machine, im_open() automatically converts the - * file for you in memory. - * - * For some large files (eg. TIFF) this may - * not be what you want, it can fill memory very quickly. Instead, you - * can either use "rd" mode (see below), or you can use the lower-level - * API and control the loading process yourself. See - * #VipsFormat. - * - * im_open() can read files in most formats. - * - * Note that "r" mode works in at least two stages. - * It should return quickly and let you check header fields. It will - * only actually read in pixels when you first access them. - * - * - * - * - * "rd" - * opens the named file for reading. If the uncompressed image is larger - * than a threshold and the file format does not support random access, - * rather than uncompressing to memory, im_open() will uncompress to a - * temporary disc file. This file will be automatically deleted when the - * IMAGE is closed. - * - * See im_system_image() for an explanation of how VIPS selects a - * location for the temporary file. - * - * The disc threshold can be set with the "--vips-disc-threshold" - * command-line argument, or the IM_DISC_THRESHOLD environment variable. - * The value is a simple integer, but can take a unit postfix of "k", - * "m" or "g" to indicate kilobytes, megabytes or gigabytes. - * - * For example: - * - * |[ - * vips --vips-disc-threshold "500m" im_copy fred.tif fred.v - * ]| - * - * will copy via disc if "fred.tif" is more than 500 Mbytes - * uncompressed. The default threshold is 100MB. - * - * - * - * - * "w" - * opens the named file for writing. It looks at the file name - * suffix to determine the type to write -- for example: - * - * |[ - * im_open( "fred.tif", "w" ) - * ]| - * - * will write in TIFF format. - * - * - * - * - * "t" - * creates a temporary memory buffer image. - * - * - * - * - * "p" - * creates a "glue" descriptor you can use to join two image - * processing operations together. - * - * - * - * - * "rw" - * opens the named file for reading and writing. This will only work for - * VIPS files in a format native to your machine. It is only for - * paintbox-type applications. - * - * - * - * - * See also: im_close(), #VipsFormat - * - * Returns: the image descriptor on success and NULL on error. - */ -IMAGE * -im_open( const char *filename, const char *mode ) -{ - IMAGE *im; - VipsFormatClass *format; - - /* Pass in a nonsense name for argv0 ... this init world is only here - * for old programs which are missing an im_init_world() call. We must - * have threads set up before we can process. - */ - if( im_init_world( "vips" ) ) - im_error_clear(); - - if( !filename || !mode ) { - im_error( "im_open", "%s", _( "NULL filename or mode" ) ); - return( NULL ); - } - - /* - - we can't use the vips handler in the format system, since we - want to be able to open the image directly rather than - copying to an existing descriptor - - if we don't do this, things like paintbox apps won't work - - */ - - switch( mode[0] ) { - case 'r': - if( (format = vips_format_for_file( filename )) ) { - if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, - "vips" ) == 0 ) { - if( !(im = im_open_vips( filename )) ) - return( NULL ); - } - else if( !(im = open_sub( format, filename, - mode[1] == 'd' )) ) - return( NULL ); - } - else - return( NULL ); - break; - - case 'w': - if( (format = vips_format_for_name( filename )) ) { - if( strcmp( VIPS_OBJECT_CLASS( format )->nickname, - "vips" ) == 0 ) - im = im_openout( filename ); - else { - if( !(im = im_open( filename, "p" )) ) - return( NULL ); - if( attach_sb( im, format->save, filename ) ) { - im_close( im ); - return( NULL ); - } - } - } - else { - char suffix[FILENAME_MAX]; - - im_filename_suffix( filename, suffix ); - im_error( "im_open", - _( "unsupported filetype \"%s\"" ), - suffix ); - - return( NULL ); - } - break; - - case 't': - im = im_setbuf( filename ); - break; - - case 'p': - im = im_partial( filename ); - break; - - default: - im_error( "im_open", _( "bad mode \"%s\"" ), mode ); - return( NULL ); - } - - /* Attach progress feedback, if required. - */ - if( im__progress || g_getenv( "IM_PROGRESS" ) ) { - Progress *progress = IM_NEW( im, Progress ); - - progress->im = im; - im_add_evalstart_callback( im, - (im_callback_fn) evalstart_cb, progress, NULL ); - im_add_eval_callback( im, - (im_callback_fn) eval_cb, progress, NULL ); - im_add_evalend_callback( im, - (im_callback_fn) evalend_cb, progress, NULL ); - } - -#ifdef DEBUG - printf( "im_open: success for %s (%p)\n", im->filename, im ); -#endif /*DEBUG*/ - - return( im ); -} - -/* Just here for compatibility. - */ -IMAGE * -im_open_header( const char *file ) -{ - return( im_open( file, "r" ) ); -} diff --git a/libvips/iofuncs/im_unmapfile.c b/libvips/iofuncs/im_unmapfile.c deleted file mode 100644 index 1c5c6df2..00000000 --- a/libvips/iofuncs/im_unmapfile.c +++ /dev/null @@ -1,71 +0,0 @@ -/* @(#) Function which unmaps a file memory mapped by mapfile() - * @(#) The argument baseaddress should be the pointer returned by mapfile(); - * @(#) The function finds the size of the file from - * @(#) - * @(#) int im_unmapfile(fd, baseaddress) - * @(#) int fd; - * @(#) char *baseaddress; - * @(#) - * @(#) Returns 0 on success and -1 on error. - * @(#) - * Copyright: Nicos Dessipris - * Wriiten on: 13/02/1990 - * Updated on: - * 18/4/97 JC - * - ANSIfied - */ - -/* - - This file is part of VIPS. - - VIPS is free software; you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - */ - -/* - - These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include -#ifdef HAVE_SYS_MMAN_H -#include -#endif -#include - -#include -#include - -#ifdef WITH_DMALLOC -#include -#endif /*WITH_DMALLOC*/ - -int -im_unmapfile( IMAGE *im ) -{ - if( im__munmap( im->baseaddr, im->length ) ) - return( -1 ); - im->baseaddr = NULL; - im->length = 0; - - return( 0 ); -} diff --git a/libvips/iofuncs/image.c b/libvips/iofuncs/image.c index 324fc5f4..334bc7c9 100644 --- a/libvips/iofuncs/image.c +++ b/libvips/iofuncs/image.c @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -319,70 +318,184 @@ enum { PROP_LAST }; +/* Our signals. + */ +enum { + SIG_PRECLOSE, + SIG_CLOSE, + SIG_POSTCLOSE, + SIG_PREEVAL, + SIG_EVAL, + SIG_POSTEVAL, + SIG_WRITTEN, + SIG_LAST +}; + +/* Progress feedback. Only really useful for testing, tbh. + */ +int im__progress = 0; + +/* A string giving the image size (in bytes of uncompressed image) above which + * we decompress to disc on open. Can be eg. "12m" for 12 megabytes. + */ +char *im__disc_threshold = NULL; + +static guint vips_image_signals[SIG_LAST] = { 0 }; + G_DEFINE_TYPE( VipsImage, vips_image, VIPS_TYPE_OBJECT ); +static int +vips_image_preclose( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + + if( !image->preclose ) { + image->preclose = TRUE; + +#ifdef DEBUG + printf( "vips_image_preclose: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_PRECLOSE], 0 ); + } +} + +static int +vips_image_close( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + + if( !image->close ) { + image->close = TRUE; + +#ifdef DEBUG + printf( "vips_image_close: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_CLOSE], 0 ); + } +} + static int vips_image_postclose( VipsImage *image ) { VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + if( !image->postclose ) { + image->postclose = TRUE; + #ifdef DEBUG - printf( "vips_image_postclose: " ); - vips_object_print( object ); + printf( "vips_image_postclose: " ); + vips_object_print( object ); #endif /*DEBUG*/ - - needs to be a true signal - - finish removing im_close() - - - - return( image_class->postclose( image ) ); + g_signal_emit( image, vips_image_signals[SIG_POSTCLOSE], 0 ); + } } - + static void vips_image_finalize( GObject *gobject ) { VipsImage *image = VIPS_IMAGE( gobject ); + /* Should be no regions defined on the image, since they all hold a + * ref to their host image. + */ + g_assert( !image->regions ); + + /* Therefore there should be no windows. + */ + g_assert( !image->windows ); + + /* Junk generate functions. + */ + image->start = NULL; + image->generate = NULL; + image->stop = NULL; + image->client1 = NULL; + image->client2 = NULL; + + /* No more upstream/downstream links. + */ + im__link_break_all( image ); + + /* Any file mapping? + */ + if( image->baseaddr ) { + /* MMAP file. + */ +#ifdef DEBUG_IO + printf( "im__close: unmapping file ..\n" ); +#endif /*DEBUG_IO*/ + + im__munmap( image->baseaddr, image->length ); + image->baseaddr = NULL; + image->length = 0; + + /* This must have been a pointer to the mmap region, rather + * than a setbuf. + */ + image->data = NULL; + } + + /* Is there a file descriptor? + */ + if( image->fd != -1 ) { +#ifdef DEBUG_IO + printf( "im__close: closing output file ..\n" ); +#endif /*DEBUG_IO*/ + + if( image->dtype == IM_OPENOUT ) + (void) im__writehist( image ); + if( close( im->fd ) == -1 ) + im_error( "im_close", + _( "unable to close fd for %s" ), + image->filename ); + im->fd = -1; + } + + /* Any image data? + */ + if( image->data ) { + /* Buffer image. Only free stuff we know we allocated. + */ + if( image->dtype == IM_SETBUF ) { +#ifdef DEBUG_IO + printf( "im__close: freeing buffer ..\n" ); +#endif /*DEBUG_IO*/ + im_free( image->data ); + image->dtype = IM_NONE; + } + + image->data = NULL; + } + + vips_image_close( image ); + VIPS_FREE( image->filename ); VIPS_FREE( image->mode ); - /* Use -1 rather than 0 for unset. - */ - if( image->fd != -1 ) { - close( image->fd ); - image->fd = -1; - } - VIPS_FREEF( g_mutex_free, image->sslock ); - most of im__close() is below: + VIPS_FREE( image->Hist ); + VIPS_FREEF( im__gslist_gvalue_free, image->history_list ); + im__meta_destroy( image ); + im__time_destroy( image ); - result |= im__trigger_callbacks( im->postclosefns ); - IM_FREEF( im_slist_free_all, im->postclosefns ); - - IM_FREEF( g_mutex_free, im->sslock ); - IM_FREE( im->filename ); - IM_FREE( im->Hist ); - IM_FREEF( im__gslist_gvalue_free, im->history_list ); - im__meta_destroy( im ); - im__time_destroy( im ); - - g_mutex_lock( im__global_lock ); - im__open_images = g_slist_remove( im__open_images, im ); - g_mutex_unlock( im__global_lock ); + vips_image_postclose( image ); G_OBJECT_CLASS( vips_image_parent_class )->finalize( gobject ); } -#ifdef VIPS_DEBUG static void vips_image_dispose( GObject *gobject ) { +#ifdef VIPS_DEBUG VIPS_DEBUG_MSG( "vips_image_dispose: " ); vips_object_print( VIPS_OBJECT( gobject ) ); +#endif /*VIPS_DEBUG*/ G_OBJECT_CLASS( vips_image_parent_class )->dispose( gobject ); } @@ -390,12 +503,53 @@ vips_image_dispose( GObject *gobject ) static void vips_image_destroy( VipsObject *object ) { +#ifdef VIPS_DEBUG VIPS_DEBUG_MSG( "vips_image_destroy: " ); vips_object_print( VIPS_OBJECT( gobject ) ); +#endif /*VIPS_DEBUG*/ + + vips_image_preclose( image ); VIPS_OBJECT_CLASS( vips_image_parent_class )->destroy( object ); } -#endif /*VIPS_DEBUG*/ + +static void +vips_image_info( VipsObject *object, VipsBuf *buf ) +{ + VipsImage *image = VIPS_IMAGE( object ); + + vips_buf_appendf( buf, "image->dtype = %d\n", image->dtype ); + vips_buf_appendf( buf, "image->demand = %s\n", + VIPS_ENUM_STRING( VIPS_TYPE_DEMAND, image->demand ) ); + vips_buf_appendf( buf, "image->magic = 0x%x\n", image->magic ); + vips_buf_appendf( buf, "image->fd = %d\n", image->fd ); + vips_buf_appendf( buf, "image->baseaddr = %p\n", image->baseaddr ); + vips_buf_appendf( buf, "image->length = %#" + G_GSIZE_MODIFIER "x\n", image->length ); + vips_buf_appendf( buf, "image->data = %p\n", image->data ); + vips_buf_appendf( buf, "image->sizeof_header = %d\n", + image->sizeof_header ); + + VIPS_OBJECT_CLASS( parent_class )->info( object, buf ); +} + +static void +vips_image_generate_caption( VipsObject *object, VipsBuf *buf ) +{ + VipsImage *image = VIPS_IMAGE( object ); + + vips_buf_appendf( buf, + ngettext( + "%dx%d %s, %d band, %s", + "%dx%d %s, %d bands, %s", image->bands ), + vips_image_get_width( image ), + vips_image_get_height( image ), + VIPS_ENUM_NICK( VIPS_TYPE_FORMAT, + vips_image_get_format( image ) ), + vips_image_get_bands( image ), + VIPS_ENUM_NICK( VIPS_TYPE_TYPE, + vips_image_get_type( image ) ) ); +} static gboolean vips_format_is_vips( VipsFormatClass *format ) @@ -403,6 +557,339 @@ vips_format_is_vips( VipsFormatClass *format ) return( strcmp( VIPS_OBJECT_CLASS( format )->nickname, "vips" ) == 0 ); } +/* Lazy open. + */ + +/* What we track during a delayed open. + */ +typedef struct { + VipsImage *out; + + VipsFormatClass *format;/* Read in pixels with this */ + gboolean disc; /* Read via disc requested */ + VipsImage *image; /* The real decompressed image */ +} Lazy; + +static void +lazy_free( Lazy *lazy ) +{ + VIPS_UNREF( lazy->image ); +} + +static Lazy * +lazy_new( VipsImage *out, VipsFormatClass *format, gboolean disc ) +{ + Lazy *lazy; + + if( !(lazy = IM_NEW( out, Lazy )) ) + return( NULL ); + lazy->out = out; + lazy->format = format; + lazy->disc = disc; + lazy->image = NULL; + g_signal_connect( out, "close", lazy_free, NULL ); + + return( lazy ); +} + +typedef struct { + const char unit; + int multiplier; +} Unit; + +static size_t +parse_size( const char *size_string ) +{ + static Unit units[] = { + { 'k', 1024 }, + { 'm', 1024 * 1024 }, + { 'g', 1024 * 1024 * 1024 } + }; + + size_t size; + int n; + int i, j; + char *unit; + + /* An easy way to alloc a buffer large enough. + */ + unit = g_strdup( size_string ); + n = sscanf( size_string, "%d %s", &i, unit ); + if( n > 0 ) + size = i; + if( n > 1 ) { + for( j = 0; j < IM_NUMBER( units ); j++ ) + if( tolower( unit[0] ) == units[j].unit ) { + size *= units[j].multiplier; + break; + } + } + g_free( unit ); + +#ifdef DEBUG + printf( "parse_size: parsed \"%s\" as %zd\n", size_string, size ); +#endif /*DEBUG*/ + + return( size ); +} + +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 = parse_size( env ); + + if( im__disc_threshold ) + threshold = parse_size( im__disc_threshold ); + +#ifdef DEBUG + printf( "disc_threshold: %zd bytes\n", threshold ); +#endif /*DEBUG*/ + } + + return( threshold ); +} + +static VipsImage * +lazy_image( Lazy *lazy ) +{ + VipsImage *image; + + /* We open to disc if: + * - 'disc' is set + * - disc_threshold() has not been set to zero + * - the format does not support lazy read + * - the image will be more than a megabyte, uncompressed + */ + image = NULL; + if( lazy->disc && + disc_threshold() && + !(vips_format_get_flags( lazy->format, lazy->out->filename ) & + VIPS_FORMAT_PARTIAL) ) { + size_t size = IM_IMAGE_SIZEOF_LINE( lazy->out ) * + lazy->out->Ysize; + + if( size > disc_threshold() ) { + if( !(image = im__open_temp( "%s.v" )) ) + return( NULL ); + +#ifdef DEBUG + printf( "lazy_image: opening to disc file \"%s\"\n", + image->filename ); +#endif /*DEBUG*/ + } + } + + /* Otherwise, fall back to a "p". + */ + if( !image && + !(image = im_open( lazy->out->filename, "p" )) ) + return( NULL ); + + return( image ); +} + +/* Our start function ... do the lazy open, if necessary, and return a region + * on the new image. + */ +static void * +open_lazy_start( VipsImage *out, void *a, void *dummy ) +{ + Lazy *lazy = (Lazy *) a; + const char *filename = lazy->out->filename; + + if( !lazy->image ) { + if( !(lazy->image = lazy_image( lazy )) || + lazy->format->load( filename, lazy->image ) || + im_pincheck( lazy->image ) ) { + VIPS_UNREF( lazy->image ); + return( NULL ); + } + } + + return( im_region_create( lazy->image ) ); +} + +/* Just copy. + */ +static int +open_lazy_generate( REGION *or, void *seq, void *a, void *b ) +{ + REGION *ir = (REGION *) seq; + + Rect *r = &or->valid; + + /* Ask for input we need. + */ + if( im_prepare( ir, r ) ) + return( -1 ); + + /* Attach output region to that. + */ + if( im_region_region( or, ir, r, r->left, r->top ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy open ... init the header with the first OpenLazyFn, delay actually + * decoding pixels with the second OpenLazyFn until the first generate(). + */ +static int +vips_open_lazy( VipsImage *out, + VipsFormatClass *format, gboolean disc, IMAGE *out ) +{ + Lazy *lazy; + + if( !(lazy = lazy_new( out, format, disc )) ) + 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( format->header( out->filename, out ) || + im_demand_hint( out, IM_THINSTRIP, NULL ) ) + return( -1 ); + + /* Then 'start' creates the real image and 'gen' paints 'out' with + * pixels from the real image on demand. + */ + if( im_generate( out, + open_lazy_start, open_lazy_generate, im_stop_one, + lazy, NULL ) ) + return( -1 ); + + return( 0 ); +} + +/* Lazy save. + */ + +/* If we write to (eg.) TIFF, actually do the write + * to a "p" and on "written" do im_vips2tiff() or whatever. Track save + * parameters here. + */ +typedef struct { + int (*save_fn)(); /* Save function */ + char *filename; /* Save args */ +} SaveBlock; + +/* From "written" callback: invoke a delayed save. + */ +static int +vips_image_save_cb( VipsImage *image, SaveBlock *sb ) +{ + if( sb->save_fn( image, sb->filename ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_attach_save( VipsImage *image, int (*save_fn)(), const char *filename ) +{ + SaveBlock *sb; + + if( (sb = IM_NEW( image, SaveBlock )) ) { + sb->save_fn = save_fn; + sb->filename = im_strdup( image, filename ); + g_signal_connect( image, "written", vips_image_save_cb, sb ); + } +} + +/* Progress feedback. + */ + +/* What we track during an eval. + */ +typedef struct { + IMAGE *image; + + int last_percent; /* The last %complete we displayed */ +} Progress; + +static int +vips_image_evalstart_cb( Progress *progress ) +{ + int tile_width; + int tile_height; + int nlines; + + progress->last_percent = 0; + + vips_get_tile_size( progress->im, &tile_width, &tile_height, &nlines ); + printf( _( "%s %s: %d threads, %d x %d tiles, groups of %d scanlines" ), + g_get_prgname(), progress->im->filename, + im_concurrency_get(), + tile_width, tile_height, nlines ); + printf( "\n" ); + + return( 0 ); +} + +static int +vips_image_eval_cb( Progress *progress ) +{ + IMAGE *im = progress->im; + + if( im->time->percent != progress->last_percent ) { + printf( _( "%s %s: %d%% complete" ), + g_get_prgname(), im->filename, im->time->percent ); + printf( "\r" ); + fflush( stdout ); + + progress->last_percent = im->time->percent; + } + + return( 0 ); +} + +static int +vips_image_evalend_cb( Progress *progress ) +{ + IMAGE *im = progress->im; + + /* Spaces at end help to erase the %complete message we overwrite. + */ + printf( _( "%s %s: done in %ds \n" ), + g_get_prgname(), im->filename, im->time->run ); + + return( 0 ); +} + +/* Attach progress feedback, if required. + */ +static void +vips_image_add_progress( VipsImage *image ) +{ + if( im__progress || + g_getenv( "IM_PROGRESS" ) ) { + + Progress *progress = IM_NEW( image, Progress ); + + progress->image = image; + g_signal_connect( image, "evalstart", + vips_image_evalstart_cb, progress ); + g_signal_connect( image, "eval", + vips_image_eval_cb, progress ); + g_signal_connect( image, "evalend", + vips_image_evalend_cb, progress ); + } +} + static int vips_image_build( VipsObject *object ) { @@ -460,7 +947,8 @@ vips_image_build( VipsObject *object ) } } else { - if( vips_open_sub( format, filename, mode[1] == 'd' ) ) + if( vips_open_lazy( image, + format, filename, mode[1] == 'd' ) ) return( -1 ); } @@ -474,9 +962,8 @@ vips_image_build( VipsObject *object ) } else { image->dtype = VIPS_IMAGE_TYPE_PARTIAL; - if( vips_attach_save( image, - format->save, filename ) ) - return( -1 ); + vips_attach_save( image, + format->save, filename ); } } else { @@ -506,52 +993,16 @@ vips_image_build( VipsObject *object ) return( -1 ); } + vips_image_add_progress( image ); + #ifdef DEBUG_VIPS - printf ("vips_image_build: "); + printf( "vips_image_build: " ); vips_object_dump( VIPS_OBJECT( image ) ); #endif /*DEBUG_VIPS*/ return( 0 ); } -static void -vips_image_info( VipsObject *object, VipsBuf *buf ) -{ - VipsImage *image = VIPS_IMAGE( object ); - - vips_buf_appendf( buf, "image->dtype = %d\n", image->dtype ); - vips_buf_appendf( buf, "image->demand = %s\n", - VIPS_ENUM_STRING( VIPS_TYPE_DEMAND, image->demand ) ); - vips_buf_appendf( buf, "image->magic = 0x%x\n", image->magic ); - vips_buf_appendf( buf, "image->fd = %d\n", image->fd ); - vips_buf_appendf( buf, "image->baseaddr = %p\n", image->baseaddr ); - vips_buf_appendf( buf, "image->length = %#" - G_GSIZE_MODIFIER "x\n", image->length ); - vips_buf_appendf( buf, "image->data = %p\n", image->data ); - vips_buf_appendf( buf, "image->sizeof_header = %d\n", - image->sizeof_header ); - - VIPS_OBJECT_CLASS( parent_class )->info( object, buf ); -} - -static void -vips_image_generate_caption( VipsObject *object, VipsBuf *buf ) -{ - VipsImage *image = VIPS_IMAGE( object ); - - vips_buf_appendf( buf, - ngettext( - "%dx%d %s, %d band, %s", - "%dx%d %s, %d bands, %s", image->bands ), - vips_image_get_width( image ), - vips_image_get_height( image ), - VIPS_ENUM_NICK( VIPS_TYPE_FORMAT, - vips_image_get_format( image ) ), - vips_image_get_bands( image ), - VIPS_ENUM_NICK( VIPS_TYPE_TYPE, - vips_image_get_type( image ) ) ); -} - static void vips_image_class_init( VipsImageClass *class ) { @@ -568,15 +1019,11 @@ vips_image_class_init( VipsImageClass *class ) im_error_clear(); gobject_class->finalize = vips_image_finalize; -#ifdef VIPS_DEBUG gobject_class->dispose = vips_image_dispose; -#endif /*VIPS_DEBUG*/ gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; -#ifdef VIPS_DEBUG vobject_class->destroy = vips_image_destroy; -#endif /*VIPS_DEBUG*/ vobject_class->info = vips_image_info; vobject_class->generate_caption = vips_image_generate_caption; vobject_class->copy_attributes = vips_image_copy_attributes; @@ -655,6 +1102,60 @@ vips_image_class_init( VipsImageClass *class ) vips_object_class_install_argument( vobject_class, pspec, VIPS_ARGUMENT_NONE, G_STRUCT_OFFSET( VipsImage, demand ) ); + + /* Create signals. + */ + vips_image_signals[SIG_PRECLOSE] = g_signal_new( "preclose", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, preclose ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + vips_image_signals[SIG_CLOSE] = g_signal_new( "close", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, close ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + vips_image_signals[SIG_POSTCLOSE] = g_signal_new( "postclose", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, postclose ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + + vips_image_signals[SIG_PREEVAL] = g_signal_new( "preeval", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, preeval ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + vips_image_signals[SIG_EVAL] = g_signal_new( "eval", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, eval ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + vips_image_signals[SIG_POSTEVAL] = g_signal_new( "posteval", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET( VipsImageClass, posteval ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); + + vips_image_signals[SIG_WRITTEN] = g_signal_new( "written", + G_TYPE_FROM_CLASS( class ), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET( VipsImageClass, written ), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0 ); } static void @@ -671,10 +1172,6 @@ vips_image_init( VipsImage *image ) image->fd = -1; /* since 0 is stdout */ image->sizeof_header = VIPS_SIZEOF_HEADER; image->sslock = g_mutex_new (); - - g_mutex_lock( im__global_lock ); - im__open_images = g_slist_prepend( im__open_images, image ); - g_mutex_unlock( im__global_lock ); } /* Set of access functions. @@ -739,3 +1236,168 @@ vips_image_get_yoffset( VipsImage *image ) { return( image->Yoffset ); } + +void +vips_image_written( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + +#ifdef DEBUG + printf( "vips_image_written: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_WRITTEN], 0 ); +} + +void +vips_image_preeval( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + +#ifdef DEBUG + printf( "vips_image_preeval: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_PREEVAL], 0 ); +} + +void +vips_image_eval( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + +#ifdef DEBUG + printf( "vips_image_eval: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_EVAL], 0 ); +} + +void +vips_image_posteval( VipsImage *image ) +{ + VipsImageClass *image_class = VIPS_IMAGE_GET_CLASS( image ); + +#ifdef DEBUG + printf( "vips_image_posteval: " ); + vips_object_print( object ); +#endif /*DEBUG*/ + + g_signal_emit( image, vips_image_signals[SIG_POSTEVAL], 0 ); +} + +/** + * im_open: + * @filename: file to open + * @mode: mode to open with + * + * im_open() examines the mode string, and creates an appropriate #IMAGE. + * + * + * + * + * "r" + * opens the named file for reading. If the file is not in the native + * VIPS format for your machine, im_open() automatically converts the + * file for you in memory. + * + * For some large files (eg. TIFF) this may + * not be what you want, it can fill memory very quickly. Instead, you + * can either use "rd" mode (see below), or you can use the lower-level + * API and control the loading process yourself. See + * #VipsFormat. + * + * im_open() can read files in most formats. + * + * Note that "r" mode works in at least two stages. + * It should return quickly and let you check header fields. It will + * only actually read in pixels when you first access them. + * + * + * + * + * "rd" + * opens the named file for reading. If the uncompressed image is larger + * than a threshold and the file format does not support random access, + * rather than uncompressing to memory, im_open() will uncompress to a + * temporary disc file. This file will be automatically deleted when the + * IMAGE is closed. + * + * See im_system_image() for an explanation of how VIPS selects a + * location for the temporary file. + * + * The disc threshold can be set with the "--vips-disc-threshold" + * command-line argument, or the IM_DISC_THRESHOLD environment variable. + * The value is a simple integer, but can take a unit postfix of "k", + * "m" or "g" to indicate kilobytes, megabytes or gigabytes. + * + * For example: + * + * |[ + * vips --vips-disc-threshold "500m" im_copy fred.tif fred.v + * ]| + * + * will copy via disc if "fred.tif" is more than 500 Mbytes + * uncompressed. The default threshold is 100MB. + * + * + * + * + * "w" + * opens the named file for writing. It looks at the file name + * suffix to determine the type to write -- for example: + * + * |[ + * im_open( "fred.tif", "w" ) + * ]| + * + * will write in TIFF format. + * + * + * + * + * "t" + * creates a temporary memory buffer image. + * + * + * + * + * "p" + * creates a "glue" descriptor you can use to join two image + * processing operations together. + * + * + * + * + * "rw" + * opens the named file for reading and writing. This will only work for + * VIPS files in a format native to your machine. It is only for + * paintbox-type applications. + * + * + * + * + * See also: im_close(), #VipsFormat + * + * Returns: the image descriptor on success and NULL on error. + */ +VipsImage * +im_open( const char *filename, const char *mode ) +{ + VipsImage *image; + + image = VIPS_IMAGE( g_object_new( VIPS_IMAGE_TYPE, NULL ) ); + g_object_set( image, + "filename", filename, + "mode", mode, + NULL ); + if( vips_object_build( image ) ) { + VIPS_UNREF( image ); + return( NULL ); + } + + return( image ); +} diff --git a/libvips/iofuncs/object.c b/libvips/iofuncs/object.c index bbb5ce57..5bfc4395 100644 --- a/libvips/iofuncs/object.c +++ b/libvips/iofuncs/object.c @@ -56,6 +56,10 @@ enum { PROP_LAST }; +/* Table of all objects, handy for debugging. + */ +static GHashTable *vips_object_all = NULL; + G_DEFINE_ABSTRACT_TYPE( VipsObject, vips_object, G_TYPE_OBJECT ); int @@ -348,6 +352,7 @@ vips_object_finalize( GObject *gobject ) vips_object_print( object ); #endif /*DEBUG*/ + g_hash_table_remove( vips_object_all, object ); IM_FREEF( vips_argument_table_destroy, object->argument_table ); G_OBJECT_CLASS( vips_object_parent_class )->finalize( gobject ); @@ -707,6 +712,10 @@ vips_object_class_init( VipsObjectClass *object_class ) GParamSpec *pspec; + if( !vips_object_all ) + vips_object_all = g_hash_table_new( + g_direct_hash, g_direct_equal ); + gobject_class->dispose = vips_object_dispose; gobject_class->finalize = vips_object_finalize; gobject_class->set_property = vips_object_set_property; @@ -762,6 +771,8 @@ vips_object_init( VipsObject *object ) printf( "vips_object_init: " ); vips_object_print( object ); #endif /*DEBUG*/ + + g_hash_table_insert( vips_object_all, object, object ); } /* Add a vipsargument ... automate some stuff with this. @@ -1044,3 +1055,32 @@ vips_object_to_string( VipsObject *object, VipsBuf *buf ) if( !first ) vips_buf_appends( buf, ")" ); } + +typdef struct { + VSListMap2Fn fn; + void *a; + void *b; + void *result; +} VipsObjectMapArgs; + +static void +vips_object_map_sub( VipsObject *object, VipsObjectMapArgs *args ) +{ + if( !args->result ) + args->result = fn( object, args->a, args->b ); +} + +void * +vips_object_map( VSListMap2Fn fn, void *a, void *b ) +{ + VipsObjectMapArgs args; + + args.fn = fn; + args.a = a; + args.b = b; + args.result = NULL; + g_hash_table_foreach( vips_object_all, + (GHFunc) vips_object_map_sub, &args ); + + return( args.result ); +}