Example #1
0
VipsImage *
im_open( const char *filename, const char *mode )
{
	VipsImage *image;

	/* Pass in a nonsense name for argv0 ... this init path is only here
	 * for old programs which are missing an vips_init() call. We need
	 * i18n set up before we can translate.
	 */
	if( vips_init( "giant_banana" ) )
		vips_error_clear();

	/* We have to go via the old VipsFormat system so we can support the
	 * "filename:option" syntax.
	 */
	if( strcmp( mode, "r" ) == 0 ||
		strcmp( mode, "rd" ) == 0 ) {
		if( !(image = vips__deprecated_open_read( filename )) )
			return( NULL );
	}
	else if( strcmp( mode, "w" ) == 0 ) {
		if( !(image = vips__deprecated_open_write( filename )) )
			return( NULL );
	}
	else {
		if( !(image = vips_image_new_mode( filename, mode )) )
			return( NULL );
	}

	return( image );
}
Example #2
0
int
main( int argc, char **argv )
{
	GOptionContext *context;
	GError *error = NULL;
	int i;

	if( vips_init( argv[0] ) )
	        vips_error_exit( "unable to start VIPS" );
	textdomain( GETTEXT_PACKAGE );
	setlocale( LC_ALL, "" );

        context = g_option_context_new( _( "- thumbnail generator" ) );

	g_option_context_add_main_entries( context, options, GETTEXT_PACKAGE );
	g_option_context_add_group( context, vips_get_option_group() );

	if( !g_option_context_parse( context, &argc, &argv, &error ) ) {
		if( error ) {
			fprintf( stderr, "%s\n", error->message );
			g_error_free( error );
		}

		vips_error_exit( "try \"%s --help\"", g_get_prgname() );
	}

	g_option_context_free( context );

	if( sscanf( thumbnail_size, "%d x %d", 
		&thumbnail_width, &thumbnail_height ) != 2 ) {
		if( sscanf( thumbnail_size, "%d", &thumbnail_width ) != 1 ) 
			vips_error_exit( "unable to parse size \"%s\" -- "
				"use eg. 128 or 200x300", thumbnail_size );

		thumbnail_height = thumbnail_width;
	}

	for( i = 1; i < argc; i++ ) {
		/* Hang resources for processing this thumbnail off @process.
		 */
		VipsObject *process = VIPS_OBJECT( vips_image_new() ); 

		if( thumbnail_process( process, argv[i] ) ) {
			fprintf( stderr, "%s: unable to thumbnail %s\n", 
				argv[0], argv[i] );
			fprintf( stderr, "%s", vips_error_buffer() );
			vips_error_clear();
		}

		g_object_unref( process );
	}

	vips_shutdown();

	return( 0 );
}
Example #3
0
/* Call this before vips stuff that uses stuff we need to have inited.
 */
void
vips_check_init( void )
{
	/* Pass in a nonsense name for argv0 ... this init path is only here
	 * for old programs which are missing an vips_init() call. We need
	 * i18n set up before we can translate.
	 */
	if( vips_init( "giant_banana" ) )
		vips_error_clear();
}
Example #4
0
/**
 * vips_error_g:
 * @error: glib error pointer
 *
 * This function sets the glib error pointer from the vips error buffer and
 * clears it. It's handy for returning errors to glib functions from vips.
 *
 * See also: g_set_error().
 */
void
vips_error_g( GError **error )
{
	static GQuark vips_domain = 0;

	if( !vips_domain ) 
		vips_domain = g_quark_from_string( "libvips" );

	g_set_error( error, vips_domain, -1, "%s", vips_error_buffer() );
	vips_error_clear();
}
Example #5
0
// just g_object_set_property(), except we allow set enum from string
static void 
set_property( VipsObject *object, const char *name, const GValue *value )
{
	VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( object );
	GType type = G_VALUE_TYPE( value );

	GParamSpec *pspec;
	VipsArgumentClass *argument_class;
	VipsArgumentInstance *argument_instance;

	if( vips_object_get_argument( object, name, 
		&pspec, &argument_class, &argument_instance ) ) {
		g_warning( "%s", vips_error_buffer() );
		vips_error_clear();
		return;
	}

	if( G_IS_PARAM_SPEC_ENUM( pspec ) &&
		type == G_TYPE_STRING ) {
		GType pspec_type = G_PARAM_SPEC_VALUE_TYPE( pspec );

		int enum_value;
		GValue value2 = { 0 }; 

		if( (enum_value = vips_enum_from_nick( object_class->nickname, 
			pspec_type, g_value_get_string( value ) )) < 0 ) {
			g_warning( "%s", vips_error_buffer() );
			vips_error_clear();
			return;
		}

		g_value_init( &value2, pspec_type ); 
		g_value_set_enum( &value2, enum_value ); 
		g_object_set_property( G_OBJECT( object ), name, &value2 );
		g_value_unset( &value2 );
	}
	else
		g_object_set_property( G_OBJECT( object ), name, value );
}
Example #6
0
/**
 * vips_error_g:
 * @error: glib error pointer
 *
 * This function sets the glib error pointer from the vips error buffer and
 * clears it. It's handy for returning errors to glib functions from vips.
 *
 * See also: g_set_error().
 */
void
vips_error_g( GError **error )
{
	static GQuark vips_domain = 0;

	if( !vips_domain ) 
		vips_domain = g_quark_from_string( "libvips" );

	/* glib does not expect a trailing '\n' and vips always has one.
	 */
	g_mutex_lock( vips__global_lock );
	vips_buf_removec( &vips_error_buf, '\n' );
	g_mutex_unlock( vips__global_lock );

	g_set_error( error, vips_domain, -1, "%s", vips_error_buffer() );
	vips_error_clear();
}
Example #7
0
int
main( int argc, char **argv )
{
	GOptionContext *context;
	GOptionGroup *main_group;
	GError *error = NULL;
	int i;
	int result;

	if( VIPS_INIT( argv[0] ) )
	        vips_error_exit( "unable to start VIPS" );
	textdomain( GETTEXT_PACKAGE );
	setlocale( LC_ALL, "" );

        context = g_option_context_new( _( "- thumbnail generator" ) );

	main_group = g_option_group_new( NULL, NULL, NULL, NULL, NULL );
	g_option_group_add_entries( main_group, options );
	vips_add_option_entries( main_group ); 
	g_option_group_set_translation_domain( main_group, GETTEXT_PACKAGE );
	g_option_context_set_main_group( context, main_group );

	if( !g_option_context_parse( context, &argc, &argv, &error ) ) {
		if( error ) {
			fprintf( stderr, "%s\n", error->message );
			g_error_free( error );
		}

		vips_error_exit( "try \"%s --help\"", g_get_prgname() );
	}

	g_option_context_free( context );

	if( sscanf( thumbnail_size, "%d x %d", 
		&thumbnail_width, &thumbnail_height ) != 2 ) {
		if( sscanf( thumbnail_size, "%d", &thumbnail_width ) != 1 ) 
			vips_error_exit( "unable to parse size \"%s\" -- "
				"use eg. 128 or 200x300", thumbnail_size );

		thumbnail_height = thumbnail_width;
	}

	if( rotate_image ) {
#ifndef HAVE_EXIF
		vips_warn( "vipsthumbnail", "%s",
			_( "auto-rotate disabled: "
			      "libvips built without exif support" ) );
#endif /*!HAVE_EXIF*/
	}

	result = 0;

	for( i = 1; i < argc; i++ ) {
		/* Hang resources for processing this thumbnail off @process.
		 */
		VipsObject *process = VIPS_OBJECT( vips_image_new() ); 

		if( thumbnail_process( process, argv[i] ) ) {
			fprintf( stderr, "%s: unable to thumbnail %s\n", 
				argv[0], argv[i] );
			fprintf( stderr, "%s", vips_error_buffer() );
			vips_error_clear();

			/* We had a conversion failure: return an error code
			 * when we finally exit.
			 */
			result = -1;
		}

		g_object_unref( process );
	}

	vips_shutdown();

	return( result );
}
Example #8
0
static VipsImage *
thumbnail_shrink( VipsObject *process, VipsImage *in )
{
	VipsImage **t = (VipsImage **) vips_object_local_array( process, 10 );
	VipsInterpretation interpretation = linear_processing ?
		VIPS_INTERPRETATION_XYZ : VIPS_INTERPRETATION_sRGB; 

	/* TRUE if we've done the import of an ICC transform and still need to
	 * export.
	 */
	gboolean have_imported;

	/* TRUE if we've premultiplied and need to unpremultiply.
	 */
	gboolean have_premultiplied;
	VipsBandFormat unpremultiplied_format;

	/* Sniff the incoming image and try to guess what the alpha max is.
	 */
	double max_alpha;

	double shrink; 

	/* RAD needs special unpacking.
	 */
	if( in->Coding == VIPS_CODING_RAD ) {
		vips_info( "vipsthumbnail", "unpacking Rad to float" );

		/* rad is scrgb.
		 */
		if( vips_rad2float( in, &t[0], NULL ) )
			return( NULL );
		in = t[0];
	}

	/* Try to guess what the maximum alpha might be.
	 */
	max_alpha = 255;
	if( in->BandFmt == VIPS_FORMAT_USHORT )
		max_alpha = 65535;

	/* In linear mode, we import right at the start. 
	 *
	 * We also have to import the whole image if it's CMYK, since
	 * vips_colourspace() (see below) doesn't know about CMYK.
	 *
	 * This is only going to work for images in device space. If you have
	 * an image in PCS which also has an attached profile, strange things
	 * will happen. 
	 */
	have_imported = FALSE;
	if( (linear_processing ||
		in->Type == VIPS_INTERPRETATION_CMYK) &&
		in->Coding == VIPS_CODING_NONE &&
		(in->BandFmt == VIPS_FORMAT_UCHAR ||
		 in->BandFmt == VIPS_FORMAT_USHORT) &&
		(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || 
		 import_profile) ) {
		if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) )
			vips_info( "vipsthumbnail", 
				"importing with embedded profile" );
		else
			vips_info( "vipsthumbnail", 
				"importing with profile %s", import_profile );

		if( vips_icc_import( in, &t[1], 
			"input_profile", import_profile,
			"embedded", TRUE,
			"pcs", VIPS_PCS_XYZ,
			NULL ) )  
			return( NULL );

		in = t[1];

		have_imported = TRUE;
	}

	/* To the processing colourspace. This will unpack LABQ as well.
	 */
	vips_info( "vipsthumbnail", "converting to processing space %s",
		vips_enum_nick( VIPS_TYPE_INTERPRETATION, interpretation ) ); 
	if( vips_colourspace( in, &t[2], interpretation, NULL ) ) 
		return( NULL ); 
	in = t[2];

	/* If there's an alpha, we have to premultiply before shrinking. See
	 * https://github.com/jcupitt/libvips/issues/291
	 */
	have_premultiplied = FALSE;
	if( in->Bands == 2 ||
		(in->Bands == 4 && in->Type != VIPS_INTERPRETATION_CMYK) ||
		in->Bands == 5 ) {
		vips_info( "vipsthumbnail", "premultiplying alpha" ); 
		if( vips_premultiply( in, &t[3], 
			"max_alpha", max_alpha,
			NULL ) ) 
			return( NULL );
		have_premultiplied = TRUE;

		/* vips_premultiply() makes a float image. When we
		 * vips_unpremultiply() below, we need to cast back to the
		 * pre-premultiply format.
		 */
		unpremultiplied_format = in->BandFmt;
		in = t[3];
	}

	shrink = calculate_shrink( in );

	if( vips_resize( in, &t[4], 1.0 / shrink, NULL ) ) 
		return( NULL );
	in = t[4];

	if( have_premultiplied ) {
		vips_info( "vipsthumbnail", "unpremultiplying alpha" ); 
		if( vips_unpremultiply( in, &t[5], 
			"max_alpha", max_alpha,
			NULL ) || 
			vips_cast( t[5], &t[6], unpremultiplied_format, NULL ) )
			return( NULL );
		in = t[6];
	}

	/* Colour management.
	 *
	 * If we've already imported, just export. Otherwise, we're in 
	 * device space and we need a combined import/export to transform to 
	 * the target space.
	 */
	if( have_imported ) { 
		if( export_profile ||
			vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
			vips_info( "vipsthumbnail", 
				"exporting to device space with a profile" );
			if( vips_icc_export( in, &t[7], 
				"output_profile", export_profile,
				NULL ) )  
				return( NULL );
			in = t[7];
		}
		else {
			vips_info( "vipsthumbnail", "converting to sRGB" );
			if( vips_colourspace( in, &t[7], 
				VIPS_INTERPRETATION_sRGB, NULL ) ) 
				return( NULL ); 
			in = t[7];
		}
	}
	else if( export_profile &&
		(vips_image_get_typeof( in, VIPS_META_ICC_NAME ) || 
		 import_profile) ) {
		VipsImage *out;

		vips_info( "vipsthumbnail", 
			"exporting with profile %s", export_profile );

		/* We first try with the embedded profile, if any, then if
		 * that fails try again with the supplied fallback profile.
		 */
		out = NULL; 
		if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
			vips_info( "vipsthumbnail", 
				"importing with embedded profile" );

			if( vips_icc_transform( in, &t[7], export_profile,
				"embedded", TRUE,
				NULL ) ) {
				vips_warn( "vipsthumbnail", 
					_( "unable to import with "
						"embedded profile: %s" ),
					vips_error_buffer() );

				vips_error_clear();
			}
			else
				out = t[7];
		}

		if( !out &&
			import_profile ) { 
			vips_info( "vipsthumbnail", 
				"importing with fallback profile" );

			if( vips_icc_transform( in, &t[7], export_profile,
				"input_profile", import_profile,
				"embedded", FALSE,
				NULL ) )  
				return( NULL );

			out = t[7];
		}

		/* If the embedded profile failed and there's no fallback or
		 * the fallback failed, out will still be NULL.
		 */
		if( out )
			in = out;
	}

	if( delete_profile &&
		vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) {
		vips_info( "vipsthumbnail", 
			"deleting profile from output image" );
		if( !vips_image_remove( in, VIPS_META_ICC_NAME ) ) 
			return( NULL );
	}

	return( in );
}
Example #9
0
/**
 * vips_init:
 * @argv0: name of application
 *
 * vips_init() starts up the world of VIPS. You should call this on
 * program startup before using any other VIPS operations. If you do not call
 * vips_init(), VIPS will call it for you when you use your first VIPS 
 * operation, but
 * it may not be able to get hold of @argv0 and VIPS may therefore be unable
 * to find its data files. It is much better to call this function yourself.
 *
 * vips_init() does approximately the following:
 *
 * <itemizedlist>
 *   <listitem> 
 *     <para>initialises any libraries that VIPS is using, including GObject
 *     and the threading system, if neccessary</para>
 *   </listitem>
 *   <listitem> 
 *     <para>guesses where the VIPS data files are and sets up
 *     internationalisation --- see vips_guess_prefix()
 *     </para>
 *   </listitem>
 *   <listitem> 
 *     <para>creates the main vips types, including VipsImage and friends
 *     </para>
 *   </listitem>
 *   <listitem> 
 *     <para>loads any plugins from $libdir/vips-x.y, where x and y are the
 *     major and minor version numbers for this VIPS.
 *     </para>
 *   </listitem>
 * </itemizedlist>
 *
 * Example:
 *
 * |[
 * int main( int argc, char **argv )
 * {
 *   if( vips_init( argv[0] ) )
 *     vips_error_exit( "unable to start VIPS" );
 *
 *   vips_shutdown();
 *
 *   return( 0 );
 * }
 * ]|
 *
 * See also: vips_shutdown(), vips_get_option_group(), vips_version(), 
 * vips_guess_prefix(), vips_guess_libdir().
 *
 * Returns: 0 on success, -1 otherwise
 */
int
vips_init( const char *argv0 )
{
	extern GType vips_system_get_type( void );

	static gboolean started = FALSE;
	static gboolean done = FALSE;
	char *prgname;
	const char *prefix;
	const char *libdir;
	char name[256];

	/* Two stage done handling: 'done' means we've completed, 'started'
	 * means we're currently initialising. Use this to prevent recursive
	 * invocation.
	 */
	if( done )
		/* Called more than once, we succeeded, just return OK.
		 */
		return( 0 );
	if( started ) 
		/* Recursive invocation, something has broken horribly.
		 * Hopefully the first init will handle it.
		 */
		return( 0 );
	started = TRUE;

#ifdef NEED_TYPE_INIT
	/* Before glib 2.36 you have to call this on startup.
	 */
	g_type_init();
#endif /*NEED_TYPE_INIT*/

	/* Older glibs need this.
	 */
#ifndef HAVE_THREAD_NEW
	if( !g_thread_supported() ) 
		g_thread_init( NULL );
#endif 

	if( !vips__global_lock )
		vips__global_lock = vips_g_mutex_new();

	VIPS_SETSTR( vips__argv0, argv0 );

	prgname = g_path_get_basename( argv0 );
	g_set_prgname( prgname );
	g_free( prgname );

	/* Try to discover our prefix. 
	 */
	if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || 
		!(libdir = vips_guess_libdir( argv0, "VIPSHOME" )) ) 
		return( -1 );

	/* Get i18n .mo files from $VIPSHOME/share/locale/.
	 */
	vips_snprintf( name, 256,
		"%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale",
		prefix );
	bindtextdomain( GETTEXT_PACKAGE, name );
	bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );

	/* Default info setting from env.
	 */
	if( g_getenv( "IM_INFO" ) ) 
		vips__info = 1;

	/* Register base vips types.
	 */
	(void) vips_image_get_type();
	(void) vips_region_get_type();
	vips__meta_init_types();
	vips__interpolate_init();
	im__format_init();

	/* Start up operator cache.
	 */
	vips__cache_init();

	/* Start up packages.
	 */
	(void) vips_system_get_type();
	vips_arithmetic_operation_init();
	vips_conversion_operation_init();
	vips_create_operation_init();
	vips_foreign_operation_init();
	vips_resample_operation_init();
	vips_colour_operation_init();
	vips_histogram_operation_init();
	vips_convolution_operation_init();

	/* Load up any plugins in the vips libdir. We don't error on failure,
	 * it's too annoying to have VIPS refuse to start because of a broken
	 * plugin.
	 */
	if( im_load_plugins( "%s/vips-%d.%d", 
		libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) {
		vips_warn( "vips_init", "%s", vips_error_buffer() );
		vips_error_clear();
	}

	/* Also load from libdir. This is old and slightly broken behaviour
	 * :-( kept for back compat convenience.
	 */
	if( im_load_plugins( "%s", libdir ) ) {
		vips_warn( "vips_init", "%s", vips_error_buffer() );
		vips_error_clear();
	}

	/* Build classes which wrap old vips7 operations.
	 */
	vips__init_wrap7_classes();

	/* Start up the buffer cache.
	 */
	vips__buffer_init();

	/* Get the run-time compiler going.
	 */
	vips_vector_init();

	/* Register vips_shutdown(). This may well not get called and many
	 * platforms don't support it anyway.
	 */
#ifdef HAVE_ATEXIT
	atexit( vips_shutdown );
#endif /*HAVE_ATEXIT*/

	done = TRUE;

	return( 0 );
}
Example #10
0
int
vips_init( const char *argv0 )
{
	extern GType vips_system_get_type( void );

	static gboolean started = FALSE;
	static gboolean done = FALSE;
	char *prgname;
	const char *prefix;
	const char *libdir;
	char name[256];

	/* Two stage done handling: 'done' means we've completed, 'started'
	 * means we're currently initialising. Use this to prevent recursive
	 * invocation.
	 */
	if( done )
		/* Called more than once, we succeeded, just return OK.
		 */
		return( 0 );
	if( started ) 
		/* Recursive invocation, something has broken horribly.
		 * Hopefully the first init will handle it.
		 */
		return( 0 );
	started = TRUE;

#ifdef HAVE_TYPE_INIT
	/* Before glib 2.36 you have to call this on startup.
	 */
	g_type_init();
#endif /*HAVE_TYPE_INIT*/

	/* Older glibs need this.
	 */
#ifndef HAVE_THREAD_NEW
	if( !g_thread_supported() ) 
		g_thread_init( NULL );
#endif 

	/* This does an unsynchronised static hash table init on first call --
	 * we have to make sure we do this single-threaded. See: 
	 * https://github.com/openslide/openslide/issues/161
	 */
	(void) g_get_language_names(); 

	if( !vips__global_lock )
		vips__global_lock = vips_g_mutex_new();

	VIPS_SETSTR( vips__argv0, argv0 );

	prgname = g_path_get_basename( argv0 );
	g_set_prgname( prgname );
	g_free( prgname );

	vips__thread_profile_attach( "main" );

	/* We can't do VIPS_GATE_START() until command-line processing
	 * happens, since vips__thread_profile may not be set yet. Call
	 * directly. 
	 */
	vips__thread_gate_start( "init: main" ); 
	vips__thread_gate_start( "init: startup" ); 

	/* Try to discover our prefix. 
	 */
	if( !(prefix = vips_guess_prefix( argv0, "VIPSHOME" )) || 
		!(libdir = vips_guess_libdir( argv0, "VIPSHOME" )) ) 
		return( -1 );

	/* Get i18n .mo files from $VIPSHOME/share/locale/.
	 */
	vips_snprintf( name, 256,
		"%s" G_DIR_SEPARATOR_S "share" G_DIR_SEPARATOR_S "locale",
		prefix );
	bindtextdomain( GETTEXT_PACKAGE, name );
	bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );

	/* Default various settings from env.
	 */
	if( g_getenv( "VIPS_INFO" ) || 
		g_getenv( "IM_INFO" ) ) 
		vips_info_set( TRUE );
	if( g_getenv( "VIPS_TRACE" ) )
		vips_cache_set_trace( TRUE );

	/* Register base vips types.
	 */
	(void) vips_image_get_type();
	(void) vips_region_get_type();
	vips__meta_init_types();
	vips__interpolate_init();
	im__format_init();

	/* Start up operator cache.
	 */
	vips__cache_init();

	/* Start up packages.
	 */
	(void) vips_system_get_type();
	vips_arithmetic_operation_init();
	vips_conversion_operation_init();
	vips_create_operation_init();
	vips_foreign_operation_init();
	vips_resample_operation_init();
	vips_colour_operation_init();
	vips_histogram_operation_init();
	vips_convolution_operation_init();
	vips_freqfilt_operation_init();
	vips_morphology_operation_init();
	vips_draw_operation_init();
	vips_mosaicing_operation_init();

	/* Load any vips8 plugins from the vips libdir. Keep going, even if
	 * some plugins fail to load. 
	 */
	(void) vips_load_plugins( "%s/vips-plugins-%d.%d", 
		libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION );

	/* Load up any vips7 plugins in the vips libdir. We don't error on 
	 * failure, it's too annoying to have VIPS refuse to start because of 
	 * a broken plugin.
	 */
	if( im_load_plugins( "%s/vips-%d.%d", 
		libdir, VIPS_MAJOR_VERSION, VIPS_MINOR_VERSION ) ) {
		vips_warn( "vips_init", "%s", vips_error_buffer() );
		vips_error_clear();
	}

	/* Also load from libdir. This is old and slightly broken behaviour
	 * :-( kept for back compat convenience.
	 */
	if( im_load_plugins( "%s", libdir ) ) {
		vips_warn( "vips_init", "%s", vips_error_buffer() );
		vips_error_clear();
	}

	/* Start up the buffer cache.
	 */
	vips__buffer_init();

	/* Get the run-time compiler going.
	 */
	vips_vector_init();

#ifdef HAVE_GSF
	/* Use this for structured file write.
	 */
	gsf_init();
#endif /*HAVE_GSF*/

	/* Register vips_shutdown(). This may well not get called and many
	 * platforms don't support it anyway.
	 */
#ifdef HAVE_ATEXIT
	atexit( vips_shutdown );
#endif /*HAVE_ATEXIT*/

#ifdef DEBUG_LEAK
	vips__image_pixels_quark = 
		g_quark_from_static_string( "vips-image-pixels" ); 
#endif /*DEBUG_LEAK*/

	done = TRUE;

	vips__thread_gate_stop( "init: startup" ); 

	return( 0 );
}