more vipsimage hacking

Moved all of im_close.c and im_open.c into the image.c class file,
various small cleanups.
This commit is contained in:
John Cupitt 2011-02-15 14:41:46 +00:00
parent cbc366cd7d
commit 62836151d4
9 changed files with 803 additions and 1196 deletions

2
TODO
View File

@ -1,5 +1,5 @@
- _build() for "t" / "p" / "w" sould do setupout / generate / etc.?
- image.c has far too many includes

View File

@ -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.

View File

@ -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*/

View File

@ -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 \

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#ifdef HAVE_IO_H
#include <io.h>
#endif /*HAVE_IO_H*/
#include <assert.h>
#include <vips/vips.h>
#include <vips/thread.h>
#include <vips/internal.h>
#include <vips/debug.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#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 );
}

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <vips/vips.h>
#include <vips/debug.h>
#include <vips/internal.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#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.
*
* <itemizedlist>
* <listitem>
* <para>
* <emphasis>"r"</emphasis>
* 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 <emphasis>"r"</emphasis> 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"rd"</emphasis>
* 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"w"</emphasis>
* 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"t"</emphasis>
* creates a temporary memory buffer image.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"p"</emphasis>
* creates a "glue" descriptor you can use to join two image
* processing operations together.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"rw"</emphasis>
* 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.
* </para>
* </listitem>
* </itemizedlist>
*
* 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" ) );
}

View File

@ -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 <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <sys/types.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <sys/stat.h>
#include <vips/vips.h>
#include <vips/internal.h>
#ifdef WITH_DMALLOC
#include <dmalloc.h>
#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 );
}

View File

@ -45,7 +45,6 @@
#include <ctype.h>
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
@ -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.
*
* <itemizedlist>
* <listitem>
* <para>
* <emphasis>"r"</emphasis>
* 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 <emphasis>"r"</emphasis> 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"rd"</emphasis>
* 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"w"</emphasis>
* 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.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"t"</emphasis>
* creates a temporary memory buffer image.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"p"</emphasis>
* creates a "glue" descriptor you can use to join two image
* processing operations together.
* </para>
* </listitem>
* <listitem>
* <para>
* <emphasis>"rw"</emphasis>
* 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.
* </para>
* </listitem>
* </itemizedlist>
*
* 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 );
}

View File

@ -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 );
}