/* foreign file formats base class * * 7/2/12 * - add support for sequential reads * 18/6/12 * - flatten alpha with vips_flatten() * 28/5/13 * - auto rshift down to 8 bits during save * 19/1/14 * - pack and unpack rad to scrgb * 18/8/14 * - fix conversion to 16-bit RGB, thanks John * 18/6/15 * - forward progress signals from load * 23/5/16 * - remove max-alpha stuff, this is now automatic * 12/6/17 * - transform cmyk->rgb if there's an embedded profile * 16/6/17 * - add page_height * 1/1/18 * - META_SEQ support moved here * 5/3/18 * - block _start if one start fails, see #893 * 1/4/18 * - drop incompatible ICC profiles before save * 24/7/21 * - add fail_on */ /* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "pforeign.h" /** * SECTION: foreign * @short_description: load and save images in a variety of formats * @stability: Stable * @see_also: image * @include: vips/vips.h * @title: VipsForeign * * This set of operations load and save images in a variety of formats. * * The operations share a base class that offers a simple way to search for a * subclass of #VipsForeign which can load a certain file (see * vips_foreign_find_load()) or buffer (see vips_foreign_find_load_buffer()), * or which could be used to save an image to a * certain file type (see vips_foreign_find_save() and * vips_foreign_find_save_buffer()). You can then run these * operations using vips_call() and friends to perform the load or save. * * vips_image_write_to_file() and vips_image_new_from_file() and friends use * these functions to automate file load and save. * * You can also invoke the operations directly, for example: * * |[ * vips_tiffsave (my_image, "frank.anything", * "compression", VIPS_FOREIGN_TIFF_COMPRESSION_JPEG, * NULL); * ]| * * To add support for a new file format to vips, simply define a new subclass * of #VipsForeignLoad or #VipsForeignSave. * * If you define a new operation which is a subclass of #VipsForeign, support * for it automatically appears in all VIPS user-interfaces. It will also be * transparently supported by vips_image_new_from_file() and friends. * * VIPS comes with VipsForeign for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, * Matlab, Radiance, RAW, FITS, WebP, SVG, PDF, GIF and VIPS. It also includes * import filters which can load with libMagick and with OpenSlide. * * ## Writing a new loader * * Add a new loader to VIPS by subclassing #VipsForeignLoad. Subclasses need to * implement at least @header(). * * @header() must set at least the header fields of @out. @load(), if defined, * must load the pixels to @real. * * The suffix list is used to select a format to save a file in, and to pick a * loader if you don't define is_a(). * * You should also define @nickname and @description in #VipsObject. * * As a complete example, here's code for a PNG loader, minus the actual * calls to libpng. * * |[ * typedef struct _VipsForeignLoadPng { * VipsForeignLoad parent_object; * * char *filename; * } VipsForeignLoadPng; * * typedef VipsForeignLoadClass VipsForeignLoadPngClass; * * G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png, * VIPS_TYPE_FOREIGN_LOAD ); * * static VipsForeignFlags * vips_foreign_load_png_get_flags_filename( const char *filename ) * { * VipsForeignFlags flags; * * flags = 0; * if( vips__png_isinterlaced( filename ) ) * flags = VIPS_FOREIGN_PARTIAL; * else * flags = VIPS_FOREIGN_SEQUENTIAL; * * return( flags ); * } * * static VipsForeignFlags * vips_foreign_load_png_get_flags( VipsForeignLoad *load ) * { * VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; * * return( vips_foreign_load_png_get_flags_filename( png->filename ) ); * } * * static int * vips_foreign_load_png_header( VipsForeignLoad *load ) * { * VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; * * if( vips__png_header( png->filename, load->out ) ) * return( -1 ); * * return( 0 ); * } * * static int * vips_foreign_load_png_load( VipsForeignLoad *load ) * { * VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; * * if( vips__png_read( png->filename, load->real ) ) * return( -1 ); * * return( 0 ); * } * * static void * vips_foreign_load_png_class_init( VipsForeignLoadPngClass *class ) * { * GObjectClass *gobject_class = G_OBJECT_CLASS( class ); * VipsObjectClass *object_class = (VipsObjectClass *) class; * VipsForeignClass *foreign_class = (VipsForeignClass *) class; * VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; * * gobject_class->set_property = vips_object_set_property; * gobject_class->get_property = vips_object_get_property; * * object_class->nickname = "pngload"; * object_class->description = _( "load png from file" ); * * foreign_class->suffs = vips__png_suffs; * * load_class->is_a = vips__png_ispng; * load_class->get_flags_filename = * vips_foreign_load_png_get_flags_filename; * load_class->get_flags = vips_foreign_load_png_get_flags; * load_class->header = vips_foreign_load_png_header; * load_class->load = vips_foreign_load_png_load; * * VIPS_ARG_STRING( class, "filename", 1, * _( "Filename" ), * _( "Filename to load from" ), * VIPS_ARGUMENT_REQUIRED_INPUT, * G_STRUCT_OFFSET( VipsForeignLoadPng, filename ), * NULL ); * } * * static void * vips_foreign_load_png_init( VipsForeignLoadPng *png ) * { * } * ]| * * ## Writing a new saver * * Call your saver in the class' @build() method after chaining up. The * prepared image should be ready for you to save in @ready. * * As a complete example, here's the code for the CSV saver, minus the calls * to the actual save routines. * * |[ * typedef struct _VipsForeignSaveCsv { * VipsForeignSave parent_object; * * char *filename; * const char *separator; * } VipsForeignSaveCsv; * * typedef VipsForeignSaveClass VipsForeignSaveCsvClass; * * G_DEFINE_TYPE( VipsForeignSaveCsv, vips_foreign_save_csv, * VIPS_TYPE_FOREIGN_SAVE ); * * static int * vips_foreign_save_csv_build( VipsObject *object ) * { * VipsForeignSave *save = (VipsForeignSave *) object; * VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; * * if( VIPS_OBJECT_CLASS( vips_foreign_save_csv_parent_class )-> * build( object ) ) * return( -1 ); * * if( vips__csv_write( save->ready, csv->filename, csv->separator ) ) * return( -1 ); * * return( 0 ); * } * * static void * vips_foreign_save_csv_class_init( VipsForeignSaveCsvClass *class ) * { * GObjectClass *gobject_class = G_OBJECT_CLASS( class ); * VipsObjectClass *object_class = (VipsObjectClass *) class; * VipsForeignClass *foreign_class = (VipsForeignClass *) class; * VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; * * gobject_class->set_property = vips_object_set_property; * gobject_class->get_property = vips_object_get_property; * * object_class->nickname = "csvsave"; * object_class->description = _( "save image to csv file" ); * object_class->build = vips_foreign_save_csv_build; * * foreign_class->suffs = vips__foreign_csv_suffs; * * save_class->saveable = VIPS_SAVEABLE_MONO; * // no need to define ->format_table, we don't want the input * // cast for us * * VIPS_ARG_STRING( class, "filename", 1, * _( "Filename" ), * _( "Filename to save to" ), * VIPS_ARGUMENT_REQUIRED_INPUT, * G_STRUCT_OFFSET( VipsForeignSaveCsv, filename ), * NULL ); * * VIPS_ARG_STRING( class, "separator", 13, * _( "Separator" ), * _( "Separator characters" ), * VIPS_ARGUMENT_OPTIONAL_INPUT, * G_STRUCT_OFFSET( VipsForeignSaveCsv, separator ), * "\t" ); * } * * static void * vips_foreign_save_csv_init( VipsForeignSaveCsv *csv ) * { * csv->separator = g_strdup( "\t" ); * } * ]| */ /* Use this to link images to the load operation that made them. */ static GQuark vips__foreign_load_operation = 0; /** * VipsForeignFlags: * @VIPS_FOREIGN_NONE: no flags set * @VIPS_FOREIGN_PARTIAL: the image may be read lazilly * @VIPS_FOREIGN_BIGENDIAN: image pixels are most-significant byte first * @VIPS_FOREIGN_SEQUENTIAL: top-to-bottom lazy reading * * Some hints about the image loader. * * #VIPS_FOREIGN_PARTIAL means that the image can be read directly from the * file without needing to be unpacked to a temporary image first. * * #VIPS_FOREIGN_SEQUENTIAL means that the loader supports lazy reading, but * only top-to-bottom (sequential) access. Formats like PNG can read sets of * scanlines, for example, but only in order. * * If neither PARTIAL or SEQUENTIAL is set, the loader only supports whole * image read. Setting both PARTIAL and SEQUENTIAL is an error. * * #VIPS_FOREIGN_BIGENDIAN means that image pixels are most-significant byte * first. Depending on the native byte order of the host machine, you may * need to swap bytes. See vips_copy(). */ G_DEFINE_ABSTRACT_TYPE( VipsForeign, vips_foreign, VIPS_TYPE_OPERATION ); static void vips_foreign_summary_class( VipsObjectClass *object_class, VipsBuf *buf ) { VipsForeignClass *class = VIPS_FOREIGN_CLASS( object_class ); VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( object_class ); VIPS_OBJECT_CLASS( vips_foreign_parent_class )-> summary_class( object_class, buf ); if( class->suffs ) { const char **p; vips_buf_appends( buf, " (" ); for( p = class->suffs; *p; p++ ) { vips_buf_appendf( buf, "%s", *p ); if( p[1] ) vips_buf_appends( buf, ", " ); } vips_buf_appends( buf, ")" ); } vips_buf_appendf( buf, ", priority=%d", class->priority ); if( operation_class->flags & VIPS_OPERATION_UNTRUSTED ) vips_buf_appendf( buf, ", untrusted" ); if( operation_class->flags & VIPS_OPERATION_BLOCKED ) vips_buf_appendf( buf, ", blocked" ); } static void vips_foreign_class_init( VipsForeignClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; object_class->nickname = "foreign"; object_class->description = _( "load and save image files" ); object_class->summary_class = vips_foreign_summary_class; } static void vips_foreign_init( VipsForeign *object ) { } /* To iterate over supported files we build a temp list of subclasses of * VipsForeign, sort by priority, iterate, and free. */ static void * file_add_class( VipsForeignClass *class, GSList **files ) { /* We exclude "rawload" as it has a different API. */ if( !vips_isprefix( "rawload", VIPS_OBJECT_CLASS( class )->nickname ) ) /* Append so we don't reverse the list of files. Sort will * not reorder items of equal priority. */ *files = g_slist_append( *files, class ); return( NULL ); } static gint file_compare( VipsForeignClass *a, VipsForeignClass *b, void *user_data ) { return( b->priority - a->priority ); } /** * vips_foreign_map: * @base: base class to search below (eg. "VipsForeignLoad") * @fn: (scope call): function to apply to each #VipsForeignClass * @a: user data * @b: user data * * Apply a function to every #VipsForeignClass that VIPS knows about. Foreigns * are presented to the function in priority order. * * Like all VIPS map functions, if @fn returns %NULL, iteration continues. If * it returns non-%NULL, iteration terminates and that value is returned. The * map function returns %NULL if all calls return %NULL. * * See also: vips_slist_map(). * * Returns: (transfer none): the result of iteration */ void * vips_foreign_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) { GSList *files; void *result; files = NULL; (void) vips_class_map_all( g_type_from_name( base ), (VipsClassMapFn) file_add_class, (void *) &files ); files = g_slist_sort( files, (GCompareFunc) file_compare ); #ifdef DEBUG { GSList *p; printf( "vips_foreign_map: search order\n" ); for( p = files; p; p = p->next ) { VipsForeignClass *class = (VipsForeignClass *) p->data; printf( "\t%s\n", VIPS_OBJECT_CLASS( class )->nickname ); } } #endif /*DEBUG*/ result = vips_slist_map2( files, fn, a, b ); g_slist_free( files ); return( result ); } /* Abstract base class for image load. */ G_DEFINE_ABSTRACT_TYPE( VipsForeignLoad, vips_foreign_load, VIPS_TYPE_FOREIGN ); static void vips_foreign_load_dispose( GObject *gobject ) { VipsForeignLoad *load = VIPS_FOREIGN_LOAD( gobject ); VIPS_UNREF( load->real ); G_OBJECT_CLASS( vips_foreign_load_parent_class )->dispose( gobject ); } static void vips_foreign_load_summary_class( VipsObjectClass *object_class, VipsBuf *buf ) { VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_CLASS( object_class ); VIPS_OBJECT_CLASS( vips_foreign_load_parent_class )-> summary_class( object_class, buf ); if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) ) { if( class->is_a ) vips_buf_appends( buf, ", is_a" ); if( class->is_a_buffer ) vips_buf_appends( buf, ", is_a_buffer" ); if( class->is_a_source ) vips_buf_appends( buf, ", is_a_source" ); if( class->get_flags ) vips_buf_appends( buf, ", get_flags" ); if( class->get_flags_filename ) vips_buf_appends( buf, ", get_flags_filename" ); if( class->header ) vips_buf_appends( buf, ", header" ); if( class->load ) vips_buf_appends( buf, ", load" ); /* You can omit ->load(), you must not omit ->header(). */ g_assert( class->header ); } } /* Can this VipsForeign open this file? */ static void * vips_foreign_find_load_sub( VipsForeignLoadClass *load_class, const char *filename, void *b ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( load_class ); VipsForeignClass *class = VIPS_FOREIGN_CLASS( load_class ); /* Ignore the buffer and source loaders. */ if( vips_ispostfix( object_class->nickname, "_buffer" ) || vips_ispostfix( object_class->nickname, "_source" ) ) return( NULL ); #ifdef DEBUG printf( "vips_foreign_find_load_sub: %s\n", VIPS_OBJECT_CLASS( class )->nickname ); #endif /*DEBUG*/ /* Try to sniff the filetype from the first few bytes, if we can, * otherwise fall back to checking the filename suffix. */ if( load_class->is_a ) { if( load_class->is_a( filename ) ) return( load_class ); #ifdef DEBUG printf( "vips_foreign_find_load_sub: is_a failed\n" ); #endif /*DEBUG*/ } else if( class->suffs ) { if( vips_filename_suffix_match( filename, class->suffs ) ) return( load_class ); } else g_warning( "loader %s has no is_a method and no suffix list", object_class->nickname ); return( NULL ); } /** * vips_foreign_find_load: * @filename: file to find a loader for * * Searches for an operation you could use to load @filename. Any trailing * options on @filename are stripped and ignored. * * See also: vips_foreign_find_load_buffer(), vips_image_new_from_file(). * * Returns: the name of an operation on success, %NULL on error */ const char * vips_foreign_find_load( const char *name ) { char filename[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; VipsForeignLoadClass *load_class; vips__filename_split8( name, filename, option_string ); /* Very common, so make a better error message for this case. */ if( !vips_existsf( "%s", filename ) ) { vips_error( "VipsForeignLoad", _( "file \"%s\" does not exist" ), name ); return( NULL ); } if( vips_isdirf( "%s", filename ) ) { vips_error( "VipsForeignLoad", _( "\"%s\" is a directory" ), name ); return( NULL ); } if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( "VipsForeignLoad", (VipsSListMap2Fn) vips_foreign_find_load_sub, (void *) filename, NULL )) ) { vips_error( "VipsForeignLoad", _( "\"%s\" is not a known file format" ), name ); return( NULL ); } #ifdef DEBUG printf( "vips_foreign_find_load: selected %s\n", VIPS_OBJECT_CLASS( load_class )->nickname ); #endif /*DEBUG*/ return( G_OBJECT_CLASS_NAME( load_class ) ); } /* Kept for compat with earlier version of the vip8 API. Use * vips_image_new_from_file() now. */ int vips_foreign_load( const char *name, VipsImage **out, ... ) { char filename[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; const char *operation_name; va_list ap; int result; vips__filename_split8( name, filename, option_string ); if( !(operation_name = vips_foreign_find_load( filename )) ) return( -1 ); va_start( ap, out ); result = vips_call_split_option_string( operation_name, option_string, ap, filename, out ); va_end( ap ); return( result ); } /* Can this VipsForeign open this buffer? */ static void * vips_foreign_find_load_buffer_sub( VipsForeignLoadClass *load_class, const void **buf, size_t *len ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( load_class ); /* Skip non-buffer loaders. */ if( !vips_ispostfix( object_class->nickname, "_buffer" ) ) return( NULL ); if( load_class->is_a_buffer ) { if( load_class->is_a_buffer( *buf, *len ) ) return( load_class ); } else g_warning( "loader %s has no is_a_buffer method", object_class->nickname ); return( NULL ); } /** * vips_foreign_find_load_buffer: * @data: (array length=size) (element-type guint8) (transfer none): start of * memory buffer * @size: (type gsize): number of bytes in @data * * Searches for an operation you could use to load a memory buffer. To see the * range of buffer loaders supported by your vips, try something like: * * vips -l | grep load_buffer * * See also: vips_image_new_from_buffer(). * * Returns: (transfer none): the name of an operation on success, %NULL on * error. */ const char * vips_foreign_find_load_buffer( const void *data, size_t size ) { VipsForeignLoadClass *load_class; if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( "VipsForeignLoad", (VipsSListMap2Fn) vips_foreign_find_load_buffer_sub, &data, &size )) ) { vips_error( "VipsForeignLoad", "%s", _( "buffer is not in a known format" ) ); return( NULL ); } return( G_OBJECT_CLASS_NAME( load_class ) ); } /* Can this VipsForeign open this source? */ static void * vips_foreign_find_load_source_sub( void *item, void *a, void *b ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( item ); VipsForeignLoadClass *load_class = VIPS_FOREIGN_LOAD_CLASS( item ); VipsSource *source = VIPS_SOURCE( a ); /* Skip non-source loaders. */ if( !vips_ispostfix( object_class->nickname, "_source" ) ) return( NULL ); if( load_class->is_a_source ) { /* We may have done a _read() rather than a _sniff() in one of * the is_a testers. Always rewind. */ (void) vips_source_rewind( source ); if( load_class->is_a_source( source ) ) return( load_class ); } else g_warning( "loader %s has no is_a_source method", object_class->nickname ); return( NULL ); } /** * vips_foreign_find_load_source: * @source: source to load from * * Searches for an operation you could use to load a source. To see the * range of source loaders supported by your vips, try something like: * * vips -l | grep load_source * * See also: vips_image_new_from_source(). * * Returns: (transfer none): the name of an operation on success, %NULL on * error. */ const char * vips_foreign_find_load_source( VipsSource *source ) { VipsForeignLoadClass *load_class; if( !(load_class = (VipsForeignLoadClass *) vips_foreign_map( "VipsForeignLoad", vips_foreign_find_load_source_sub, source, NULL )) ) { vips_error( "VipsForeignLoad", "%s", _( "source is not in a known format" ) ); return( NULL ); } /* All source loaders should be NOCACHE. */ g_assert( VIPS_OPERATION_CLASS( load_class )->flags & VIPS_OPERATION_NOCACHE ); return( G_OBJECT_CLASS_NAME( load_class ) ); } /** * vips_foreign_is_a: * @loader: name of loader to use for test * @filename: file to test * * Return %TRUE if @filename can be loaded by @loader. @loader is something * like "tiffload" or "VipsForeignLoadTiff". * * Returns: %TRUE if @filename can be loaded by @loader. */ gboolean vips_foreign_is_a( const char *loader, const char *filename ) { const VipsObjectClass *class; VipsForeignLoadClass *load_class; if( !(class = vips_class_find( "VipsForeignLoad", loader )) ) return( FALSE ); load_class = VIPS_FOREIGN_LOAD_CLASS( class ); if( load_class->is_a && load_class->is_a( filename ) ) return( TRUE ); return( FALSE ); } /** * vips_foreign_is_a_buffer: * @loader: name of loader to use for test * @data: (array length=size) (element-type guint8): pointer to the buffer to test * @size: (type gsize): size of the buffer to test * * Return %TRUE if @data can be loaded by @loader. @loader is something * like "tiffload_buffer" or "VipsForeignLoadTiffBuffer". * * Returns: %TRUE if @data can be loaded by @loader. */ gboolean vips_foreign_is_a_buffer( const char *loader, const void *data, size_t size ) { const VipsObjectClass *class; VipsForeignLoadClass *load_class; if( !(class = vips_class_find( "VipsForeignLoad", loader )) ) return( FALSE ); load_class = VIPS_FOREIGN_LOAD_CLASS( class ); if( load_class->is_a_buffer && load_class->is_a_buffer( data, size ) ) return( TRUE ); return( FALSE ); } /** * vips_foreign_is_a_source: * @loader: name of loader to use for test * @source: source to test * * Return %TRUE if @source can be loaded by @loader. @loader is something * like "tiffload_source" or "VipsForeignLoadTiffSource". * * Returns: %TRUE if @data can be loaded by @source. */ gboolean vips_foreign_is_a_source( const char *loader, VipsSource *source ) { const VipsObjectClass *class; VipsForeignLoadClass *load_class; if( !(class = vips_class_find( "VipsForeignLoad", loader )) ) return( FALSE ); load_class = VIPS_FOREIGN_LOAD_CLASS( class ); if( load_class->is_a_source && load_class->is_a_source( source ) ) return( TRUE ); return( FALSE ); } /** * vips_foreign_flags: * @loader: name of loader to use for test * @filename: file to test * * Return the flags for @filename using @loader. * @loader is something like "tiffload" or "VipsForeignLoadTiff". * * Returns: the flags for @filename. */ VipsForeignFlags vips_foreign_flags( const char *loader, const char *filename ) { const VipsObjectClass *class; if( (class = vips_class_find( "VipsForeignLoad", loader )) ) { VipsForeignLoadClass *load_class = VIPS_FOREIGN_LOAD_CLASS( class ); if( load_class->get_flags_filename ) return( load_class->get_flags_filename( filename ) ); } return( 0 ); } static VipsObject * vips_foreign_load_new_from_string( const char *string ) { const char *file_op; GType type; VipsForeignLoad *load; if( !(file_op = vips_foreign_find_load( string )) ) return( NULL ); type = g_type_from_name( file_op ); g_assert( type ); load = VIPS_FOREIGN_LOAD( g_object_new( type, NULL ) ); g_object_set( load, "filename", string, NULL ); return( VIPS_OBJECT( load ) ); } static VipsImage * vips_foreign_load_temp( VipsForeignLoad *load ) { const guint64 disc_threshold = vips_get_disc_threshold(); const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out ); /* ->memory used to be called ->disc and default TRUE. If it's been * forced FALSE, set memory TRUE. */ if( !load->disc ) load->memory = TRUE; if( load->memory ) { #ifdef DEBUG printf( "vips_foreign_load_temp: forced memory temp\n" ); #endif /*DEBUG*/ return( vips_image_new_memory() ); } /* If this is a partial operation, we can open directly. */ if( load->flags & VIPS_FOREIGN_PARTIAL ) { #ifdef DEBUG printf( "vips_foreign_load_temp: partial temp\n" ); #endif /*DEBUG*/ return( vips_image_new() ); } /* If it can do sequential access and it's been requested, we can open * directly. */ if( (load->flags & VIPS_FOREIGN_SEQUENTIAL) && load->access != VIPS_ACCESS_RANDOM ) { #ifdef DEBUG printf( "vips_foreign_load_temp: partial sequential temp\n" ); #endif /*DEBUG*/ return( vips_image_new() ); } /* We open via disc if the uncompressed image will be larger than * vips_get_disc_threshold() */ if( image_size > disc_threshold ) { #ifdef DEBUG printf( "vips_foreign_load_temp: disc temp\n" ); #endif /*DEBUG*/ return( vips_image_new_temp_file( "%s.v" ) ); } #ifdef DEBUG printf( "vips_foreign_load_temp: fallback memory temp\n" ); #endif /*DEBUG*/ /* Otherwise, fall back to a memory buffer. */ return( vips_image_new_memory() ); } /* Check two images for compatibility: their geometries need to match. */ static gboolean vips_foreign_load_iscompat( VipsImage *a, VipsImage *b ) { if( a->Xsize != b->Xsize || a->Ysize != b->Ysize || a->Bands != b->Bands || a->Coding != b->Coding || a->BandFmt != b->BandFmt ) { vips_error( "VipsForeignLoad", "%s", _( "images do not match" ) ); return( FALSE ); } return( TRUE ); } /* Our start function ... do the lazy open, if necessary, and return a region * on the new image. */ static void * vips_foreign_load_start( VipsImage *out, void *a, void *b ) { VipsForeignLoad *load = VIPS_FOREIGN_LOAD( b ); VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( load ); /* If this start has failed before in another thread, we can fail now. */ if( load->error ) return( NULL ); if( !load->real ) { if( !(load->real = vips_foreign_load_temp( load )) ) return( NULL ); #ifdef DEBUG printf( "vips_foreign_load_start: triggering ->load()\n" ); #endif /*DEBUG*/ /* Read the image in. This may involve a long computation and * will finish with load->real holding the decompressed image. * * We want our caller to be able to see this computation on * @out, so eval signals on ->real need to appear on ->out. */ load->real->progress_signal = load->out; /* Note the load object on the image. Loaders can use * this to signal invalidate if they hit a load error. See * vips_foreign_load_invalidate() below. */ g_object_set_qdata( G_OBJECT( load->real ), vips__foreign_load_operation, load ); /* Load the image and check the result. * * ->header() read the header into @out, load will read the * image into @real. They must match exactly in size, bands, * format and coding for the copy to work. * * Some versions of ImageMagick give different results between * Ping and Load for some formats, for example. * * If the load fails, we need to stop */ if( class->load( load ) || vips_image_pio_input( load->real ) || !vips_foreign_load_iscompat( load->real, out ) ) { vips_operation_invalidate( VIPS_OPERATION( load ) ); load->error = TRUE; return( NULL ); } /* We have to tell vips that out depends on real. We've set * the demand hint below, but not given an input there. */ if( vips_image_pipelinev( load->out, load->out->dhint, load->real, NULL ) ) return( NULL ); } return( vips_region_new( load->real ) ); } /* Just pointer-copy. */ static int vips_foreign_load_generate( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop ) { VipsRegion *ir = (VipsRegion *) seq; VipsRect *r = &or->valid; /* Ask for input we need. */ if( vips_region_prepare( ir, r ) ) return( -1 ); /* Attach output region to that. */ if( vips_region_region( or, ir, r, r->left, r->top ) ) return( -1 ); return( 0 ); } static int vips_foreign_load_build( VipsObject *object ) { VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); VipsForeignLoad *load = VIPS_FOREIGN_LOAD( object ); VipsForeignLoadClass *fclass = VIPS_FOREIGN_LOAD_GET_CLASS( object ); VipsForeignFlags flags; gboolean sequential; #ifdef DEBUG printf( "vips_foreign_load_build:\n" ); #endif /*DEBUG*/ flags = 0; if( fclass->get_flags ) flags |= fclass->get_flags( load ); if( (flags & VIPS_FOREIGN_PARTIAL) && (flags & VIPS_FOREIGN_SEQUENTIAL) ) { g_warning( "%s", _( "VIPS_FOREIGN_PARTIAL and VIPS_FOREIGN_SEQUENTIAL " "both set -- using SEQUENTIAL" ) ); flags ^= VIPS_FOREIGN_PARTIAL; } g_object_set( load, "flags", flags, NULL ); /* Seq access has been requested, or the loader requires seq. */ sequential = (load->flags & VIPS_FOREIGN_SEQUENTIAL) || load->access != VIPS_ACCESS_RANDOM; /* We must block caching of seq loads. */ if( sequential ) load->nocache = TRUE; /* The deprecated "fail" field sets fail_on warning. */ if( vips_object_argument_isset( object, "fail" ) && !vips_object_argument_isset( object, "fail_on" ) ) load->fail_on = load->fail ? VIPS_FAIL_ON_WARNING : VIPS_FAIL_ON_NONE; if( VIPS_OBJECT_CLASS( vips_foreign_load_parent_class )-> build( object ) ) return( -1 ); if( load->sequential ) g_warning( "%s", _( "ignoring deprecated \"sequential\" mode -- " "please use \"access\" instead" ) ); g_object_set( object, "out", vips_image_new(), NULL ); vips_image_set_string( load->out, VIPS_META_LOADER, class->nickname ); #ifdef DEBUG printf( "vips_foreign_load_build: triggering ->header()\n" ); #endif /*DEBUG*/ /* Read the header into @out. */ if( fclass->header && fclass->header( load ) ) return( -1 ); /* If there's no ->load() method then the header read has done * everything. Otherwise, it's just set fields and we must also * load pixels. * * Delay the load until the first pixel is requested by doing the work * in the start function of the copy. */ if( fclass->load ) { #ifdef DEBUG printf( "vips_foreign_load_build: delaying read ...\n" ); #endif /*DEBUG*/ /* ->header() should set the dhint. It'll default to the safe * SMALLTILE if header() did not set it. */ if( vips_image_pipelinev( load->out, load->out->dhint, NULL ) ) return( -1 ); /* Then 'start' creates the real image and 'gen' fetches * pixels for @out from @real on demand. */ if( vips_image_generate( load->out, vips_foreign_load_start, vips_foreign_load_generate, vips_stop_one, NULL, load ) ) return( -1 ); } /* Tell downstream if we are reading sequentially. */ if( sequential ) vips_image_set_int( load->out, VIPS_META_SEQUENTIAL, 1 ); return( 0 ); } static VipsOperationFlags vips_foreign_load_operation_get_flags( VipsOperation *operation ) { VipsForeignLoad *load = VIPS_FOREIGN_LOAD( operation ); VipsOperationFlags flags; flags = VIPS_OPERATION_CLASS( vips_foreign_load_parent_class )-> get_flags( operation ); if( load->nocache ) flags |= VIPS_OPERATION_NOCACHE; return( flags ); } static void vips_foreign_load_class_init( VipsForeignLoadClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsOperationClass *operation_class = (VipsOperationClass *) class; gobject_class->dispose = vips_foreign_load_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; object_class->build = vips_foreign_load_build; object_class->summary_class = vips_foreign_load_summary_class; object_class->new_from_string = vips_foreign_load_new_from_string; object_class->nickname = "load"; object_class->description = _( "loaders" ); operation_class->get_flags = vips_foreign_load_operation_get_flags; VIPS_ARG_IMAGE( class, "out", 2, _( "Output" ), _( "Output image" ), VIPS_ARGUMENT_REQUIRED_OUTPUT, G_STRUCT_OFFSET( VipsForeignLoad, out ) ); VIPS_ARG_FLAGS( class, "flags", 106, _( "Flags" ), _( "Flags for this file" ), VIPS_ARGUMENT_OPTIONAL_OUTPUT, G_STRUCT_OFFSET( VipsForeignLoad, flags ), VIPS_TYPE_FOREIGN_FLAGS, VIPS_FOREIGN_NONE ); VIPS_ARG_BOOL( class, "memory", 107, _( "Memory" ), _( "Force open via memory" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignLoad, memory ), FALSE ); VIPS_ARG_ENUM( class, "access", 108, _( "Access" ), _( "Required access pattern for this file" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignLoad, access ), VIPS_TYPE_ACCESS, VIPS_ACCESS_RANDOM ); VIPS_ARG_ENUM( class, "fail_on", 109, _( "Fail on" ), _( "Error level to fail on" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignLoad, fail_on ), VIPS_TYPE_FAIL_ON, VIPS_FAIL_ON_NONE ); VIPS_ARG_BOOL( class, "sequential", 110, _( "Sequential" ), _( "Sequential read only" ), VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignLoad, sequential ), FALSE ); VIPS_ARG_BOOL( class, "fail", 111, _( "Fail" ), _( "Fail on first warning" ), VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignLoad, fail ), FALSE ); VIPS_ARG_BOOL( class, "disc", 112, _( "Disc" ), _( "Open to disc" ), VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignLoad, disc ), TRUE ); } static void vips_foreign_load_init( VipsForeignLoad *load ) { load->disc = TRUE; load->access = VIPS_ACCESS_RANDOM; load->fail_on = VIPS_FAIL_ON_NONE; } /* * Loaders can call this */ /** * vips_foreign_load_invalidate: (method) * @image: image to invalidate * * Loaders can call this on the image they are making if they see a read error * from the load library. It signals "invalidate" on the load operation and * will cause it to be dropped from cache. * * If we know a file will cause a read error, we don't want to cache the * failing operation, we want to make sure the image will really be opened * again if our caller tries again. For example, a broken file might be * replaced by a working one. */ void vips_foreign_load_invalidate( VipsImage *image ) { VipsOperation *operation; #ifdef DEBUG printf( "vips_foreign_load_invalidate: %p\n", image ); #endif /*DEBUG*/ if( (operation = g_object_get_qdata( G_OBJECT( image ), vips__foreign_load_operation )) ) { vips_operation_invalidate( operation ); } } /* Abstract base class for image savers. */ G_DEFINE_ABSTRACT_TYPE( VipsForeignSave, vips_foreign_save, VIPS_TYPE_FOREIGN ); static void vips_foreign_save_dispose( GObject *gobject ) { VipsForeignSave *save = VIPS_FOREIGN_SAVE( gobject ); VIPS_UNREF( save->ready ); G_OBJECT_CLASS( vips_foreign_save_parent_class )->dispose( gobject ); } static void vips_foreign_save_summary_class( VipsObjectClass *object_class, VipsBuf *buf ) { VipsForeignSaveClass *class = VIPS_FOREIGN_SAVE_CLASS( object_class ); VIPS_OBJECT_CLASS( vips_foreign_save_parent_class )-> summary_class( object_class, buf ); vips_buf_appendf( buf, ", %s", vips_enum_nick( VIPS_TYPE_SAVEABLE, class->saveable ) ); } static VipsObject * vips_foreign_save_new_from_string( const char *string ) { const char *file_op; GType type; VipsForeignSave *save; if( !(file_op = vips_foreign_find_save( string )) ) return( NULL ); type = g_type_from_name( file_op ); g_assert( type ); save = VIPS_FOREIGN_SAVE( g_object_new( type, NULL ) ); g_object_set( save, "filename", string, NULL ); return( VIPS_OBJECT( save ) ); } /* Convert an image for saving. */ int vips__foreign_convert_saveable( VipsImage *in, VipsImage **ready, VipsSaveable saveable, VipsBandFormat *format, VipsCoding *coding, VipsArrayDouble *background ) { /* in holds a reference to the output of our chain as we build it. */ g_object_ref( in ); /* For coded images, can this class save the coding we are in now? * Nothing to do. */ if( in->Coding != VIPS_CODING_NONE && coding[in->Coding] ) { *ready = in; return( 0 ); } /* For uncoded images, if this saver supports ANY bands and this * format we have nothing to do. */ if( in->Coding == VIPS_CODING_NONE && saveable == VIPS_SAVEABLE_ANY && format[in->BandFmt] == in->BandFmt ) { *ready = in; return( 0 ); } /* Otherwise ... we need to decode and then (possibly) recode at the * end. */ /* If this is an VIPS_CODING_LABQ, we can go straight to RGB. */ if( in->Coding == VIPS_CODING_LABQ ) { VipsImage *out; if( vips_LabQ2sRGB( in, &out, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* If this is an VIPS_CODING_RAD, we unpack to float. This could be * scRGB or XYZ. */ if( in->Coding == VIPS_CODING_RAD ) { VipsImage *out; if( vips_rad2float( in, &out, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* If the saver supports RAD, we need to go to scRGB or XYZ. */ if( coding[VIPS_CODING_RAD] ) { if( in->Type != VIPS_INTERPRETATION_scRGB && in->Type != VIPS_INTERPRETATION_XYZ ) { VipsImage *out; if( vips_colourspace( in, &out, VIPS_INTERPRETATION_scRGB, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } } /* If this image is CMYK and the saver is RGB-only, use lcms to try to * import to XYZ. */ if( in->Type == VIPS_INTERPRETATION_CMYK && in->Bands >= 4 && (saveable == VIPS_SAVEABLE_RGB || saveable == VIPS_SAVEABLE_RGBA || saveable == VIPS_SAVEABLE_RGBA_ONLY) ) { VipsImage *out; if( vips_icc_import( in, &out, "pcs", VIPS_PCS_XYZ, "embedded", TRUE, "input_profile", "cmyk", NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* If this is something other than CMYK or RAD, and it's not already * an RGB image, eg. maybe a LAB image, we need to transform * to RGB. */ if( !coding[VIPS_CODING_RAD] && in->Bands >= 3 && in->Type != VIPS_INTERPRETATION_CMYK && in->Type != VIPS_INTERPRETATION_sRGB && in->Type != VIPS_INTERPRETATION_RGB16 && in->Type != VIPS_INTERPRETATION_scRGB && vips_colourspace_issupported( in ) && (saveable == VIPS_SAVEABLE_RGB || saveable == VIPS_SAVEABLE_RGBA || saveable == VIPS_SAVEABLE_RGBA_ONLY || saveable == VIPS_SAVEABLE_RGB_CMYK) ) { VipsImage *out; VipsInterpretation interpretation; /* Do we make RGB or RGB16? We don't want to squash a 16-bit * RGB down to 8 bits if the saver supports 16. */ if( vips_band_format_is8bit( format[in->BandFmt] ) ) interpretation = VIPS_INTERPRETATION_sRGB; else interpretation = VIPS_INTERPRETATION_RGB16; if( vips_colourspace( in, &out, interpretation, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* VIPS_SAVEABLE_RGBA_ONLY does not support mono types ... convert * to sRGB. */ if( !coding[VIPS_CODING_RAD] && in->Bands < 3 && saveable == VIPS_SAVEABLE_RGBA_ONLY ) { VipsImage *out; VipsInterpretation interpretation; /* Do we make RGB or RGB16? We don't want to squash a 16-bit * RGB down to 8 bits if the saver supports 16. */ if( vips_band_format_is8bit( format[in->BandFmt] ) ) interpretation = VIPS_INTERPRETATION_sRGB; else interpretation = VIPS_INTERPRETATION_RGB16; if( vips_colourspace( in, &out, interpretation, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* Get the bands right. We must do this after all colourspace * transforms, since they can change the number of bands. */ if( in->Coding == VIPS_CODING_NONE ) { /* Do we need to flatten out an alpha channel? There needs to * be an alpha there now, and this writer needs to not support * alpha. */ if( (in->Bands == 2 || (in->Bands == 4 && in->Type != VIPS_INTERPRETATION_CMYK)) && (saveable == VIPS_SAVEABLE_MONO || saveable == VIPS_SAVEABLE_RGB || saveable == VIPS_SAVEABLE_RGB_CMYK) ) { VipsImage *out; if( vips_flatten( in, &out, "background", background, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* Other alpha removal strategies ... just drop the extra * bands. */ else if( in->Bands > 3 && (saveable == VIPS_SAVEABLE_RGB || (saveable == VIPS_SAVEABLE_RGB_CMYK && in->Type != VIPS_INTERPRETATION_CMYK)) ) { VipsImage *out; /* Don't let 4 bands though unless the image really is * a CMYK. * * Consider a RGBA png being saved as JPG. We can * write CMYK jpg, but we mustn't do that for RGBA * images. */ if( vips_extract_band( in, &out, 0, "n", 3, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } else if( in->Bands > 4 && ((saveable == VIPS_SAVEABLE_RGB_CMYK && in->Type == VIPS_INTERPRETATION_CMYK) || saveable == VIPS_SAVEABLE_RGBA || saveable == VIPS_SAVEABLE_RGBA_ONLY) ) { VipsImage *out; if( vips_extract_band( in, &out, 0, "n", 4, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } else if( in->Bands > 1 && saveable == VIPS_SAVEABLE_MONO ) { VipsImage *out; if( vips_extract_band( in, &out, 0, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* Else we have VIPS_SAVEABLE_ANY and we don't chop bands down. */ } /* Handle the ushort interpretations. * * RGB16 and GREY16 use 0-65535 for black-white. If we have an image * tagged like this, and it has more than 8 bits (we leave crazy uchar * images tagged as RGB16 alone), we'll need to get it ready for the * saver. */ if( (in->Type == VIPS_INTERPRETATION_RGB16 || in->Type == VIPS_INTERPRETATION_GREY16) && !vips_band_format_is8bit( in->BandFmt ) ) { /* If the saver supports ushort, cast to ushort. It may be * float at the moment, for example. * * If the saver does not support ushort, automatically shift * it down. This is the behaviour we want for saving an RGB16 * image as JPG, for example. */ if( format[VIPS_FORMAT_USHORT] == VIPS_FORMAT_USHORT ) { VipsImage *out; if( vips_cast( in, &out, VIPS_FORMAT_USHORT, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } else { VipsImage *out; if( vips_rshift_const1( in, &out, 8, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; /* That could have produced an int image ... make sure * we are now uchar. */ if( vips_cast( in, &out, VIPS_FORMAT_UCHAR, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } } /* Cast to the output format. */ { VipsImage *out; if( vips_cast( in, &out, format[in->BandFmt], NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* Does this class want a coded image? Search the coding table for the * first one. */ if( coding[VIPS_CODING_NONE] ) { /* Already NONE, nothing to do. */ } else if( coding[VIPS_CODING_LABQ] ) { VipsImage *out; if( vips_Lab2LabQ( in, &out, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } else if( coding[VIPS_CODING_RAD] ) { VipsImage *out; if( vips_float2rad( in, &out, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; } /* Some format libraries, like libpng, will throw a hard error if the * profile is inappropriate for this image type. With profiles inherited * from a source image, this can happen all the time, so we * want to silently drop the profile in this case. */ if( vips_image_get_typeof( in, VIPS_META_ICC_NAME ) ) { const void *data; size_t length; if( !vips_image_get_blob( in, VIPS_META_ICC_NAME, &data, &length ) && !vips_icc_is_compatible_profile( in, data, length ) ) { VipsImage *out; if( vips_copy( in, &out, NULL ) ) { g_object_unref( in ); return( -1 ); } g_object_unref( in ); in = out; vips_image_remove( in, VIPS_META_ICC_NAME ); } } *ready = in; return( 0 ); } static int vips_foreign_save_build( VipsObject *object ) { VipsForeignSave *save = VIPS_FOREIGN_SAVE( object ); if( save->in ) { VipsForeignSaveClass *class = VIPS_FOREIGN_SAVE_GET_CLASS( save ); VipsImage *ready; if( vips__foreign_convert_saveable( save->in, &ready, class->saveable, class->format_table, class->coding, save->background ) ) return( -1 ); if( save->page_height ) { VipsImage *x; if( vips_copy( ready, &x, NULL ) ) { VIPS_UNREF( ready ); return( -1 ); } VIPS_UNREF( ready ); ready = x; vips_image_set_int( ready, VIPS_META_PAGE_HEIGHT, save->page_height ); } VIPS_UNREF( save->ready ); save->ready = ready; } if( VIPS_OBJECT_CLASS( vips_foreign_save_parent_class )-> build( object ) ) return( -1 ); return( 0 ); } #define UC VIPS_FORMAT_UCHAR #define C VIPS_FORMAT_CHAR #define US VIPS_FORMAT_USHORT #define S VIPS_FORMAT_SHORT #define UI VIPS_FORMAT_UINT #define I VIPS_FORMAT_INT #define F VIPS_FORMAT_FLOAT #define X VIPS_FORMAT_COMPLEX #define D VIPS_FORMAT_DOUBLE #define DX VIPS_FORMAT_DPCOMPLEX static int vips_foreign_save_format_table[10] = { // UC C US S UI I F X D DX UC, C, US, S, UI, I, F, X, D, DX }; static void vips_foreign_save_class_init( VipsForeignSaveClass *class ) { GObjectClass *gobject_class = G_OBJECT_CLASS( class ); VipsObjectClass *object_class = (VipsObjectClass *) class; VipsOperationClass *operation_class = (VipsOperationClass *) class; int i; gobject_class->dispose = vips_foreign_save_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; object_class->build = vips_foreign_save_build; object_class->summary_class = vips_foreign_save_summary_class; object_class->new_from_string = vips_foreign_save_new_from_string; object_class->nickname = "save"; object_class->description = _( "savers" ); /* All savers are sequential by definition. Things like tiled tiff * write and interlaced png write, which are not, add extra caches * on their input. */ operation_class->flags |= VIPS_OPERATION_SEQUENTIAL; /* Must not cache savers. */ operation_class->flags |= VIPS_OPERATION_NOCACHE; /* Default to no coding allowed. */ for( i = 0; i < VIPS_CODING_LAST; i++ ) class->coding[i] = FALSE; class->coding[VIPS_CODING_NONE] = TRUE; /* Default to no cast on save. */ class->format_table = vips_foreign_save_format_table; VIPS_ARG_IMAGE( class, "in", 0, _( "Input" ), _( "Image to save" ), VIPS_ARGUMENT_REQUIRED_INPUT, G_STRUCT_OFFSET( VipsForeignSave, in ) ); VIPS_ARG_BOOL( class, "strip", 100, _( "Strip" ), _( "Strip all metadata from image" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSave, strip ), FALSE ); VIPS_ARG_BOXED( class, "background", 101, _( "Background" ), _( "Background value" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSave, background ), VIPS_TYPE_ARRAY_DOUBLE ); VIPS_ARG_INT( class, "page_height", 102, _( "Page height" ), _( "Set page height for multipage save" ), VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSave, page_height ), 0, VIPS_MAX_COORD, 0 ); } static void vips_foreign_save_init( VipsForeignSave *save ) { save->background = vips_array_double_newv( 1, 0.0 ); } /* Can we write this filename with this class? */ static void * vips_foreign_find_save_sub( VipsForeignSaveClass *save_class, const char *filename, void *b ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( save_class ); VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class ); const char **p; /* All savers needs suffs defined since we use the suff to pick the * saver. */ if( !class->suffs ) g_warning( "no suffix defined for %s", object_class->nickname ); /* Skip non-file savers. */ if( vips_ispostfix( object_class->nickname, "_buffer" ) || vips_ispostfix( object_class->nickname, "_target" ) ) return( NULL ); /* vips_foreign_find_save() has already removed any options from the * end of the filename, so we can test directly against the suffix. */ for( p = class->suffs; *p; p++ ) if( vips_iscasepostfix( filename, *p ) ) return( save_class ); return( NULL ); } /** * vips_foreign_find_save: * @filename: name to find a saver for * * Searches for an operation you could use to write to @filename. * Any trailing options on @filename are stripped and ignored. * * See also: vips_foreign_find_save_buffer(), vips_image_write_to_file(). * * Returns: (nullable): the name of an operation on success, %NULL on error */ const char * vips_foreign_find_save( const char *name ) { char filename[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; VipsForeignSaveClass *save_class; vips__filename_split8( name, filename, option_string ); if( !(save_class = (VipsForeignSaveClass *) vips_foreign_map( "VipsForeignSave", (VipsSListMap2Fn) vips_foreign_find_save_sub, (void *) filename, NULL )) ) { vips_error( "VipsForeignSave", _( "\"%s\" is not a known file format" ), name ); return( NULL ); } return( G_OBJECT_CLASS_NAME( save_class ) ); } static void * vips_foreign_get_suffixes_count_cb( VipsForeignSaveClass *save_class, void *a, void *b ) { VipsForeignClass *foreign_class = VIPS_FOREIGN_CLASS( save_class ); int *n_fields = (int *) a; int i; if( foreign_class->suffs ) for( i = 0; foreign_class->suffs[i]; i++ ) *n_fields += 1; return( NULL ); } static void * vips_foreign_get_suffixes_add_cb( VipsForeignSaveClass *save_class, void *a, void *b ) { VipsForeignClass *foreign_class = VIPS_FOREIGN_CLASS( save_class ); gchar ***p = (gchar ***) a; int i; if( foreign_class->suffs ) for( i = 0; foreign_class->suffs[i]; i++ ) { **p = g_strdup( foreign_class->suffs[i] ); *p += 1; } return( NULL ); } /** * vips_foreign_get_suffixes: * * Get a %NULL-terminated array listing all the supported suffixes. * * This is not the same as all the supported file types, since libvips * detects image format for load by testing the first few bytes. * * Use vips_foreign_find_load() to detect type for a specific file. * * Free the return result with g_strfreev(). * * Returns: (transfer full) (array): all supported file extensions, as a * %NULL-terminated array. */ gchar ** vips_foreign_get_suffixes( void ) { int n_suffs; gchar **suffs; gchar **p; n_suffs = 0; (void) vips_foreign_map( "VipsForeignSave", (VipsSListMap2Fn) vips_foreign_get_suffixes_count_cb, &n_suffs, NULL ); suffs = g_new0( gchar *, n_suffs + 1 ); p = suffs; (void) vips_foreign_map( "VipsForeignSave", (VipsSListMap2Fn) vips_foreign_get_suffixes_add_cb, &p, NULL ); return( suffs ); } /* Kept for early vips8 API compat. */ int vips_foreign_save( VipsImage *in, const char *name, ... ) { char filename[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; const char *operation_name; va_list ap; int result; vips__filename_split8( name, filename, option_string ); if( !(operation_name = vips_foreign_find_save( filename )) ) return( -1 ); va_start( ap, name ); result = vips_call_split_option_string( operation_name, option_string, ap, in, filename ); va_end( ap ); return( result ); } /* Can this class write this filetype to a target? */ static void * vips_foreign_find_save_target_sub( VipsForeignSaveClass *save_class, const char *suffix, void *b ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( save_class ); VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class ); /* All concrete savers needs suffs, since we use the suff to pick the * saver. */ if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) && !class->suffs ) g_warning( "no suffix defined for %s", object_class->nickname ); if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) && class->suffs && vips_ispostfix( object_class->nickname, "_target" ) && vips_filename_suffix_match( suffix, class->suffs ) ) return( save_class ); return( NULL ); } /** * vips_foreign_find_save_target: * @suffix: format to find a saver for * * Searches for an operation you could use to write to a target in @suffix * format. * * See also: vips_image_write_to_buffer(). * * Returns: (nullable): the name of an operation on success, %NULL on error */ const char * vips_foreign_find_save_target( const char *name ) { char suffix[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; VipsForeignSaveClass *save_class; vips__filename_split8( name, suffix, option_string ); if( !(save_class = (VipsForeignSaveClass *) vips_foreign_map( "VipsForeignSave", (VipsSListMap2Fn) vips_foreign_find_save_target_sub, (void *) suffix, NULL )) ) { vips_error( "VipsForeignSave", _( "\"%s\" is not a known target format" ), name ); return( NULL ); } return( G_OBJECT_CLASS_NAME( save_class ) ); } /* Can we write this buffer with this file type? */ static void * vips_foreign_find_save_buffer_sub( VipsForeignSaveClass *save_class, const char *suffix, void *b ) { VipsObjectClass *object_class = VIPS_OBJECT_CLASS( save_class ); VipsForeignClass *class = VIPS_FOREIGN_CLASS( save_class ); /* All concrete savers needs suffs, since we use the suff to pick the * saver. */ if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) && !class->suffs ) g_warning( "no suffix defined for %s", object_class->nickname ); if( !G_TYPE_IS_ABSTRACT( G_TYPE_FROM_CLASS( class ) ) && class->suffs && vips_ispostfix( object_class->nickname, "_buffer" ) && vips_filename_suffix_match( suffix, class->suffs ) ) return( save_class ); return( NULL ); } /** * vips_foreign_find_save_buffer: * @suffix: name to find a saver for * * Searches for an operation you could use to write to a buffer in @suffix * format. * * See also: vips_image_write_to_buffer(). * * Returns: (nullable): the name of an operation on success, %NULL on error */ const char * vips_foreign_find_save_buffer( const char *name ) { char suffix[VIPS_PATH_MAX]; char option_string[VIPS_PATH_MAX]; VipsForeignSaveClass *save_class; vips__filename_split8( name, suffix, option_string ); if( !(save_class = (VipsForeignSaveClass *) vips_foreign_map( "VipsForeignSave", (VipsSListMap2Fn) vips_foreign_find_save_buffer_sub, (void *) suffix, NULL )) ) { vips_error( "VipsForeignSave", _( "\"%s\" is not a known buffer format" ), name ); return( NULL ); } return( G_OBJECT_CLASS_NAME( save_class ) ); } /* C API wrappers for loadable modules go here. */ /** * vips_heifload: * @filename: file to load * @out: (out): decompressed image * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, page (top-level image number) to read * * @n: %gint, load this many pages * * @thumbnail: %gboolean, fetch thumbnail instead of image * * @unlimited: %gboolean, remove all denial of service limits * * Read a HEIF image file into a VIPS image. * * Use @page to select a page to render, numbering from zero. If neither @n * nor @page are set, @page defaults to the primary page, otherwise to 0. * * Use @n to select the number of pages to render. The default is 1. Pages are * rendered in a vertical column. Set to -1 to mean "until the end of the * document". Use vips_grid() to reorganise pages. * * HEIF images have a primary image. The metadata item `heif-primary` gives * the page number of the primary. * * If @thumbnail is %TRUE, then fetch a stored thumbnail rather than the * image. * * By default, input image dimensions are limited to 16384x16384. * If @unlimited is %TRUE, this increases to the maximum of 65535x65535. * * The bitdepth of the heic image is recorded in the metadata item * `heif-bitdepth`. * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. */ int vips_heifload( const char *filename, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "heifload", ap, filename, out ); va_end( ap ); return( result ); } /** * vips_heifload_buffer: * @buf: (array length=len) (element-type guint8): memory area to load * @len: (type gsize): size of memory area * @out: (out): image to write * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, page (top-level image number) to read * * @n: %gint, load this many pages * * @thumbnail: %gboolean, fetch thumbnail instead of image * * @unlimited: %gboolean, remove all denial of service limits * * Read a HEIF image file into a VIPS image. * Exactly as vips_heifload(), but read from a memory buffer. * * You must not free the buffer while @out is active. The * #VipsObject::postclose signal on @out is a good place to free. * * See also: vips_heifload(). * * Returns: 0 on success, -1 on error. */ int vips_heifload_buffer( void *buf, size_t len, VipsImage **out, ... ) { va_list ap; VipsBlob *blob; int result; /* We don't take a copy of the data or free it. */ blob = vips_blob_new( NULL, buf, len ); va_start( ap, out ); result = vips_call_split( "heifload_buffer", ap, blob, out ); va_end( ap ); vips_area_unref( VIPS_AREA( blob ) ); return( result ); } /** * vips_heifload_source: * @source: source to load from * @out: (out): image to write * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, page (top-level image number) to read * * @n: %gint, load this many pages * * @thumbnail: %gboolean, fetch thumbnail instead of image * * @unlimited: %gboolean, remove all denial of service limits * * Exactly as vips_heifload(), but read from a source. * * See also: vips_heifload(). * * Returns: 0 on success, -1 on error. */ int vips_heifload_source( VipsSource *source, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "heifload_source", ap, source, out ); va_end( ap ); return( result ); } /** * vips_heifsave: (method) * @in: image to save * @filename: file to write to * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @Q: %gint, quality factor * * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits * * @lossless: %gboolean, enable lossless encoding * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode * * Write a VIPS image to a file in HEIF format. * * Use @Q to set the compression factor. Default 50, which seems to be roughly * what the iphone uses. Q 30 gives about the same quality as JPEG Q 75. * * Set @lossless %TRUE to switch to lossless compression. * * Use @compression to set the encoder e.g. HEVC, AVC, AV1. It defaults to AV1 * if the target filename ends with ".avif", otherwise HEVC. * * Use @effort to control the CPU effort spent improving compression. * This is currently only applicable to AV1 encoders. Defaults to 4, 0 is * fastest, 9 is slowest. * * Chroma subsampling is normally automatically disabled for Q >= 90. You can * force the subsampling mode with @subsample_mode. * * Use @bitdepth to set the bitdepth of the output file. HEIC supports at * least 8, 10 and 12 bits; other codecs may support more or fewer options. * * See also: vips_image_write_to_file(), vips_heifload(). * * Returns: 0 on success, -1 on error. */ int vips_heifsave( VipsImage *in, const char *filename, ... ) { va_list ap; int result; va_start( ap, filename ); result = vips_call_split( "heifsave", ap, in, filename ); va_end( ap ); return( result ); } /** * vips_heifsave_buffer: (method) * @in: image to save * @buf: (array length=len) (element-type guint8): return output buffer here * @len: (type gsize): return output length here * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @Q: %gint, quality factor * * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits * * @lossless: %gboolean, enable lossless encoding * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode * * As vips_heifsave(), but save to a memory buffer. * * The address of the buffer is returned in @obuf, the length of the buffer in * @olen. You are responsible for freeing the buffer with g_free() when you * are done with it. * * See also: vips_heifsave(), vips_image_write_to_file(). * * Returns: 0 on success, -1 on error. */ int vips_heifsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) { va_list ap; VipsArea *area; int result; area = NULL; va_start( ap, len ); result = vips_call_split( "heifsave_buffer", ap, in, &area ); va_end( ap ); if( !result && area ) { if( buf ) { *buf = area->data; area->free_fn = NULL; } if( len ) *len = area->length; vips_area_unref( area ); } return( result ); } /** * vips_heifsave_target: (method) * @in: image to save * @target: save image to this target * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @Q: %gint, quality factor * * @bitdepth: %gint, set write bit depth to 8, 10, or 12 bits * * @lossless: %gboolean, enable lossless encoding * * @compression: #VipsForeignHeifCompression, write with this compression * * @effort: %gint, encoding effort * * @subsample_mode: #VipsForeignSubsample, chroma subsampling mode * * As vips_heifsave(), but save to a target. * * See also: vips_heifsave(), vips_image_write_to_target(). * * Returns: 0 on success, -1 on error. */ int vips_heifsave_target( VipsImage *in, VipsTarget *target, ... ) { va_list ap; int result; va_start( ap, target ); result = vips_call_split( "heifsave_target", ap, in, target ); va_end( ap ); return( result ); } /** * vips_jxlload: * @filename: file to load * @out: (out): decompressed image * @...: %NULL-terminated list of optional named arguments * * Read a JPEG-XL image. * * The JPEG-XL loader and saver are experimental features and may change * in future libvips versions. * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. */ int vips_jxlload( const char *filename, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "jxlload", ap, filename, out ); va_end( ap ); return( result ); } /** * vips_jxlload_buffer: * @buf: (array length=len) (element-type guint8): memory area to load * @len: (type gsize): size of memory area * @out: (out): image to write * @...: %NULL-terminated list of optional named arguments * * Exactly as vips_jxlload(), but read from a buffer. * * Returns: 0 on success, -1 on error. */ int vips_jxlload_buffer( void *buf, size_t len, VipsImage **out, ... ) { va_list ap; VipsBlob *blob; int result; /* We don't take a copy of the data or free it. */ blob = vips_blob_new( NULL, buf, len ); va_start( ap, out ); result = vips_call_split( "jxlload_buffer", ap, blob, out ); va_end( ap ); vips_area_unref( VIPS_AREA( blob ) ); return( result ); } /** * vips_jxlload_source: * @source: source to load from * @out: (out): decompressed image * @...: %NULL-terminated list of optional named arguments * * Exactly as vips_jxlload(), but read from a source. * * Returns: 0 on success, -1 on error. */ int vips_jxlload_source( VipsSource *source, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "jxlload_source", ap, source, out ); va_end( ap ); return( result ); } /** * vips_jxlsave: (method) * @in: image to save * @filename: file to write to * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @tier: %gint, decode speed tier * * @distance: %gdouble, maximum encoding error * * @effort: %gint, encoding effort * * @lossless: %gboolean, enables lossless compression * * @Q: %gint, quality setting * * Write a VIPS image to a file in JPEG-XL format. * * The JPEG-XL loader and saver are experimental features and may change * in future libvips versions. * * @tier sets the overall decode speed the encoder will target. Minimum is 0 * (highest quality), and maximum is 4 (lowest quality). Default is 0. * * @distance sets the target maximum encoding error. Minimum is 0 * (highest quality), and maximum is 15 (lowest quality). Default is 1.0 * (visually lossless). * * As a convenience, you can also use @Q to set @distance. @Q uses * approximately the same scale as regular JPEG. * * Set @lossless to enable lossless compresion. * * Returns: 0 on success, -1 on error. */ int vips_jxlsave( VipsImage *in, const char *filename, ... ) { va_list ap; int result; va_start( ap, filename ); result = vips_call_split( "jxlsave", ap, in, filename ); va_end( ap ); return( result ); } /** * vips_jxlsave_buffer: (method) * @in: image to save * @buf: (array length=len) (element-type guint8): return output buffer here * @len: (type gsize): return output length here * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @tier: %gint, decode speed tier * * @distance: %gdouble, maximum encoding error * * @effort: %gint, encoding effort * * @lossless: %gboolean, enables lossless compression * * @Q: %gint, quality setting * * As vips_jxlsave(), but save to a memory buffer. * * See also: vips_jxlsave(), vips_image_write_to_target(). * * Returns: 0 on success, -1 on error. */ int vips_jxlsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) { va_list ap; VipsArea *area; int result; area = NULL; va_start( ap, len ); result = vips_call_split( "jxlsave_buffer", ap, in, &area ); va_end( ap ); if( !result && area ) { if( buf ) { *buf = area->data; area->free_fn = NULL; } if( len ) *len = area->length; vips_area_unref( area ); } return( result ); } /** * vips_jxlsave_target: (method) * @in: image to save * @target: save image to this target * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @tier: %gint, decode speed tier * * @distance: %gdouble, maximum encoding error * * @effort: %gint, encoding effort * * @lossless: %gboolean, enables lossless compression * * @Q: %gint, quality setting * * As vips_jxlsave(), but save to a target. * * See also: vips_jxlsave(), vips_image_write_to_target(). * * Returns: 0 on success, -1 on error. */ int vips_jxlsave_target( VipsImage *in, VipsTarget *target, ... ) { va_list ap; int result; va_start( ap, target ); result = vips_call_split( "jxlsave_target", ap, in, target ); va_end( ap ); return( result ); } /** * vips_pdfload: * @filename: file to load * @out: (out): output image * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, load this page, numbered from zero * * @n: %gint, load this many pages * * @dpi: %gdouble, render at this DPI * * @scale: %gdouble, scale render by this factor * * @background: #VipsArrayDouble background colour * * @password: %gchararray background colour * * Render a PDF file into a VIPS image. * * The output image is always RGBA --- CMYK PDFs will be * converted. If you need CMYK bitmaps, you should use vips_magickload() * instead. * * Use @page to select a page to render, numbering from zero. * * Use @n to select the number of pages to render. The default is 1. Pages are * rendered in a vertical column, with each individual page aligned to the * left. Set to -1 to mean "until the end of the document". Use vips_grid() * to change page layout. * * Use @dpi to set the rendering resolution. The default is 72. Additionally, * you can scale by setting @scale. If you set both, they combine. * * Use @background to set the background RGBA colour. The default is 255 * (solid white), use eg. 0 for a transparent background. * * Use @password to supply a decryption password. * * The operation fills a number of header fields with metadata, for example * "pdf-author". They may be useful. * * This function only reads the image header and does not render any pixel * data. Rendering occurs when pixels are accessed. * * See also: vips_image_new_from_file(), vips_magickload(). * * Returns: 0 on success, -1 on error. */ int vips_pdfload( const char *filename, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "pdfload", ap, filename, out ); va_end( ap ); return( result ); } /** * vips_pdfload_buffer: * @buf: (array length=len) (element-type guint8): memory area to load * @len: (type gsize): size of memory area * @out: (out): image to write * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, load this page, numbered from zero * * @n: %gint, load this many pages * * @dpi: %gdouble, render at this DPI * * @scale: %gdouble, scale render by this factor * * @background: #VipsArrayDouble background colour * * Read a PDF-formatted memory buffer into a VIPS image. Exactly as * vips_pdfload(), but read from memory. * * You must not free the buffer while @out is active. The * #VipsObject::postclose signal on @out is a good place to free. * * See also: vips_pdfload(). * * Returns: 0 on success, -1 on error. */ int vips_pdfload_buffer( void *buf, size_t len, VipsImage **out, ... ) { va_list ap; VipsBlob *blob; int result; /* We don't take a copy of the data or free it. */ blob = vips_blob_new( NULL, buf, len ); va_start( ap, out ); result = vips_call_split( "pdfload_buffer", ap, blob, out ); va_end( ap ); vips_area_unref( VIPS_AREA( blob ) ); return( result ); } /** * vips_pdfload_source: * @source: source to load from * @out: (out): image to write * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @page: %gint, load this page, numbered from zero * * @n: %gint, load this many pages * * @dpi: %gdouble, render at this DPI * * @scale: %gdouble, scale render by this factor * * @background: #VipsArrayDouble background colour * * Exactly as vips_pdfload(), but read from a source. * * See also: vips_pdfload() * * Returns: 0 on success, -1 on error. */ int vips_pdfload_source( VipsSource *source, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "pdfload_source", ap, source, out ); va_end( ap ); return( result ); } /** * vips_openslideload: * @filename: file to load * @out: (out): decompressed image * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @level: %gint, load this level * * @associated: %gchararray, load this associated image * * @attach_associated: %gboolean, attach all associated images as metadata * * @autocrop: %gboolean, crop to image bounds * * Read a virtual slide supported by the OpenSlide library into a VIPS image. * OpenSlide supports images in Aperio, Hamamatsu, MIRAX, Sakura, Trestle, * and Ventana formats. * * To facilitate zooming, virtual slide formats include multiple scaled-down * versions of the high-resolution image. These are typically called * "levels". By default, vips_openslideload() reads the highest-resolution * level (level 0). Set @level to the level number you want. * * In addition to the slide image itself, virtual slide formats sometimes * include additional images, such as a scan of the slide's barcode. * OpenSlide calls these "associated images". To read an associated image, * set @associated to the image's name. * A slide's associated images are listed in the * "slide-associated-images" metadata item. * * If you set @attach_associated, then all associated images are attached as * metadata items. Use vips_image_get_image() on @out to retrieve them. Images * are attached as "openslide-associated-XXXXX", where XXXXX is the name of the * associated image. * * The output of this operator is always RGBA. * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. */ int vips_openslideload( const char *filename, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "openslideload", ap, filename, out ); va_end( ap ); return( result ); } /** * vips_openslideload_source: * @source: source to load from * @out: (out): decompressed image * @...: %NULL-terminated list of optional named arguments * * Optional arguments: * * * @level: %gint, load this level * * @associated: %gchararray, load this associated image * * @attach_associated: %gboolean, attach all associated images as metadata * * @autocrop: %gboolean, crop to image bounds * * Exactly as vips_openslideload(), but read from a source. * * Returns: 0 on success, -1 on error. */ int vips_openslideload_source( VipsSource *source, VipsImage **out, ... ) { va_list ap; int result; va_start( ap, out ); result = vips_call_split( "openslideload_source", ap, source, out ); va_end( ap ); return( result ); } /* Called from iofuncs to init all operations in this dir. Use a plugin system * instead? */ void vips_foreign_operation_init( void ) { extern GType vips_foreign_load_rad_file_get_type( void ); extern GType vips_foreign_load_rad_buffer_get_type( void ); extern GType vips_foreign_load_rad_source_get_type( void ); extern GType vips_foreign_save_rad_file_get_type( void ); extern GType vips_foreign_save_rad_buffer_get_type( void ); extern GType vips_foreign_save_rad_target_get_type( void ); extern GType vips_foreign_load_mat_get_type( void ); extern GType vips_foreign_load_ppm_file_get_type( void ); extern GType vips_foreign_load_ppm_source_get_type( void ); extern GType vips_foreign_save_ppm_file_get_type( void ); extern GType vips_foreign_save_pbm_target_get_type( void ); extern GType vips_foreign_save_pgm_target_get_type( void ); extern GType vips_foreign_save_ppm_target_get_type( void ); extern GType vips_foreign_save_pfm_target_get_type( void ); extern GType vips_foreign_load_png_file_get_type( void ); extern GType vips_foreign_load_png_buffer_get_type( void ); extern GType vips_foreign_load_png_source_get_type( void ); extern GType vips_foreign_save_png_file_get_type( void ); extern GType vips_foreign_save_png_buffer_get_type( void ); extern GType vips_foreign_save_png_target_get_type( void ); extern GType vips_foreign_save_spng_file_get_type( void ); extern GType vips_foreign_save_spng_buffer_get_type( void ); extern GType vips_foreign_save_spng_target_get_type( void ); extern GType vips_foreign_load_csv_file_get_type( void ); extern GType vips_foreign_load_csv_source_get_type( void ); extern GType vips_foreign_save_csv_file_get_type( void ); extern GType vips_foreign_save_csv_target_get_type( void ); extern GType vips_foreign_load_matrix_file_get_type( void ); extern GType vips_foreign_load_matrix_source_get_type( void ); extern GType vips_foreign_save_matrix_file_get_type( void ); extern GType vips_foreign_save_matrix_target_get_type( void ); extern GType vips_foreign_print_matrix_get_type( void ); extern GType vips_foreign_load_fits_file_get_type( void ); extern GType vips_foreign_load_fits_source_get_type( void ); extern GType vips_foreign_save_fits_get_type( void ); extern GType vips_foreign_load_analyze_get_type( void ); extern GType vips_foreign_load_openexr_get_type( void ); extern GType vips_foreign_load_openslide_file_get_type( void ); extern GType vips_foreign_load_openslide_source_get_type( void ); extern GType vips_foreign_load_vips_file_get_type( void ); extern GType vips_foreign_load_vips_source_get_type( void ); extern GType vips_foreign_save_vips_file_get_type( void ); extern GType vips_foreign_save_vips_target_get_type( void ); extern GType vips_foreign_load_jpeg_file_get_type( void ); extern GType vips_foreign_load_jpeg_buffer_get_type( void ); extern GType vips_foreign_load_jpeg_source_get_type( void ); extern GType vips_foreign_save_jpeg_file_get_type( void ); extern GType vips_foreign_save_jpeg_buffer_get_type( void ); extern GType vips_foreign_save_jpeg_target_get_type( void ); extern GType vips_foreign_save_jpeg_mime_get_type( void ); extern GType vips_foreign_load_tiff_file_get_type( void ); extern GType vips_foreign_load_tiff_buffer_get_type( void ); extern GType vips_foreign_load_tiff_source_get_type( void ); extern GType vips_foreign_save_tiff_file_get_type( void ); extern GType vips_foreign_save_tiff_buffer_get_type( void ); extern GType vips_foreign_save_tiff_target_get_type( void ); extern GType vips_foreign_load_raw_get_type( void ); extern GType vips_foreign_save_raw_get_type( void ); extern GType vips_foreign_save_raw_fd_get_type( void ); extern GType vips_foreign_load_magick_file_get_type( void ); extern GType vips_foreign_load_magick_buffer_get_type( void ); extern GType vips_foreign_load_magick7_file_get_type( void ); extern GType vips_foreign_load_magick7_buffer_get_type( void ); extern GType vips_foreign_save_magick_file_get_type( void ); extern GType vips_foreign_save_magick_buffer_get_type( void ); extern GType vips_foreign_save_magick_bmp_file_get_type( void ); extern GType vips_foreign_save_magick_bmp_buffer_get_type( void ); extern GType vips_foreign_save_magick_gif_file_get_type( void ); extern GType vips_foreign_save_magick_gif_buffer_get_type( void ); extern GType vips_foreign_save_dz_file_get_type( void ); extern GType vips_foreign_save_dz_buffer_get_type( void ); extern GType vips_foreign_save_dz_target_get_type( void ); extern GType vips_foreign_load_webp_file_get_type( void ); extern GType vips_foreign_load_webp_buffer_get_type( void ); extern GType vips_foreign_load_webp_source_get_type( void ); extern GType vips_foreign_save_webp_file_get_type( void ); extern GType vips_foreign_save_webp_buffer_get_type( void ); extern GType vips_foreign_save_webp_target_get_type( void ); extern GType vips_foreign_load_pdf_file_get_type( void ); extern GType vips_foreign_load_pdf_buffer_get_type( void ); extern GType vips_foreign_load_pdf_source_get_type( void ); extern GType vips_foreign_load_svg_file_get_type( void ); extern GType vips_foreign_load_svg_buffer_get_type( void ); extern GType vips_foreign_load_svg_source_get_type( void ); extern GType vips_foreign_load_jp2k_file_get_type( void ); extern GType vips_foreign_load_jp2k_buffer_get_type( void ); extern GType vips_foreign_load_jp2k_source_get_type( void ); extern GType vips_foreign_save_jp2k_file_get_type( void ); extern GType vips_foreign_save_jp2k_buffer_get_type( void ); extern GType vips_foreign_save_jp2k_target_get_type( void ); extern GType vips_foreign_load_jxl_file_get_type( void ); extern GType vips_foreign_load_jxl_buffer_get_type( void ); extern GType vips_foreign_load_jxl_source_get_type( void ); extern GType vips_foreign_save_jxl_file_get_type( void ); extern GType vips_foreign_save_jxl_buffer_get_type( void ); extern GType vips_foreign_save_jxl_target_get_type( void ); extern GType vips_foreign_load_heif_file_get_type( void ); extern GType vips_foreign_load_heif_buffer_get_type( void ); extern GType vips_foreign_load_heif_source_get_type( void ); extern GType vips_foreign_save_heif_file_get_type( void ); extern GType vips_foreign_save_heif_buffer_get_type( void ); extern GType vips_foreign_save_heif_target_get_type( void ); extern GType vips_foreign_save_avif_target_get_type( void ); extern GType vips_foreign_load_nifti_file_get_type( void ); extern GType vips_foreign_load_nifti_source_get_type( void ); extern GType vips_foreign_save_nifti_get_type( void ); extern GType vips_foreign_load_nsgif_file_get_type( void ); extern GType vips_foreign_load_nsgif_buffer_get_type( void ); extern GType vips_foreign_load_nsgif_source_get_type( void ); extern GType vips_foreign_save_cgif_file_get_type( void ); extern GType vips_foreign_save_cgif_buffer_get_type( void ); extern GType vips_foreign_save_cgif_target_get_type( void ); vips_foreign_load_csv_file_get_type(); vips_foreign_load_csv_source_get_type(); vips_foreign_save_csv_file_get_type(); vips_foreign_save_csv_target_get_type(); vips_foreign_load_matrix_file_get_type(); vips_foreign_load_matrix_source_get_type(); vips_foreign_save_matrix_file_get_type(); vips_foreign_save_matrix_target_get_type(); vips_foreign_print_matrix_get_type(); vips_foreign_load_raw_get_type(); vips_foreign_save_raw_get_type(); vips_foreign_save_raw_fd_get_type(); vips_foreign_load_vips_file_get_type(); vips_foreign_load_vips_source_get_type(); vips_foreign_save_vips_file_get_type(); vips_foreign_save_vips_target_get_type(); #ifdef HAVE_ANALYZE vips_foreign_load_analyze_get_type(); #endif /*HAVE_ANALYZE*/ #ifdef HAVE_PPM vips_foreign_load_ppm_file_get_type(); vips_foreign_load_ppm_source_get_type(); vips_foreign_save_ppm_file_get_type(); vips_foreign_save_pbm_target_get_type(); vips_foreign_save_pgm_target_get_type(); vips_foreign_save_ppm_target_get_type(); vips_foreign_save_pfm_target_get_type(); #endif /*HAVE_PPM*/ #ifdef HAVE_RADIANCE vips_foreign_load_rad_file_get_type(); vips_foreign_load_rad_buffer_get_type(); vips_foreign_load_rad_source_get_type(); vips_foreign_save_rad_file_get_type(); vips_foreign_save_rad_buffer_get_type(); vips_foreign_save_rad_target_get_type(); #endif /*HAVE_RADIANCE*/ #if defined(HAVE_POPPLER) && !defined(POPPLER_MODULE) vips_foreign_load_pdf_file_get_type(); vips_foreign_load_pdf_buffer_get_type(); vips_foreign_load_pdf_source_get_type(); #endif /*defined(HAVE_POPPLER) && !defined(POPPLER_MODULE)*/ #ifdef HAVE_PDFIUM vips_foreign_load_pdf_file_get_type(); vips_foreign_load_pdf_buffer_get_type(); vips_foreign_load_pdf_source_get_type(); #endif /*HAVE_PDFIUM*/ #ifdef HAVE_RSVG vips_foreign_load_svg_file_get_type(); vips_foreign_load_svg_buffer_get_type(); vips_foreign_load_svg_source_get_type(); #endif /*HAVE_RSVG*/ #if defined(HAVE_LIBJXL) && !defined(LIBJXL_MODULE) vips_foreign_load_jxl_file_get_type(); vips_foreign_load_jxl_buffer_get_type(); vips_foreign_load_jxl_source_get_type(); vips_foreign_save_jxl_file_get_type(); vips_foreign_save_jxl_buffer_get_type(); vips_foreign_save_jxl_target_get_type(); #endif /*defined(HAVE_LIBJXL) && !defined(LIBJXL_MODULE)*/ #ifdef HAVE_LIBOPENJP2 vips_foreign_load_jp2k_file_get_type(); vips_foreign_load_jp2k_buffer_get_type(); vips_foreign_load_jp2k_source_get_type(); vips_foreign_save_jp2k_file_get_type(); vips_foreign_save_jp2k_buffer_get_type(); vips_foreign_save_jp2k_target_get_type(); #endif /*HAVE_LIBOPENJP2*/ #ifdef HAVE_NSGIF vips_foreign_load_nsgif_file_get_type(); vips_foreign_load_nsgif_buffer_get_type(); vips_foreign_load_nsgif_source_get_type(); #endif /*HAVE_NSGIF*/ #ifdef HAVE_CGIF vips_foreign_save_cgif_file_get_type(); vips_foreign_save_cgif_buffer_get_type(); vips_foreign_save_cgif_target_get_type(); #endif /*HAVE_CGIF*/ #ifdef HAVE_GSF vips_foreign_save_dz_file_get_type(); vips_foreign_save_dz_buffer_get_type(); vips_foreign_save_dz_target_get_type(); #endif /*HAVE_GSF*/ #ifdef HAVE_PNG vips_foreign_load_png_file_get_type(); vips_foreign_load_png_buffer_get_type(); vips_foreign_load_png_source_get_type(); vips_foreign_save_png_file_get_type(); vips_foreign_save_png_buffer_get_type(); vips_foreign_save_png_target_get_type(); #endif /*HAVE_PNG*/ #ifdef HAVE_SPNG vips_foreign_load_png_file_get_type(); vips_foreign_load_png_buffer_get_type(); vips_foreign_load_png_source_get_type(); vips_foreign_save_spng_file_get_type(); vips_foreign_save_spng_buffer_get_type(); vips_foreign_save_spng_target_get_type(); #endif /*HAVE_SPNG*/ #ifdef HAVE_MATIO vips_foreign_load_mat_get_type(); #endif /*HAVE_MATIO*/ #ifdef HAVE_JPEG vips_foreign_load_jpeg_file_get_type(); vips_foreign_load_jpeg_buffer_get_type(); vips_foreign_load_jpeg_source_get_type(); vips_foreign_save_jpeg_file_get_type(); vips_foreign_save_jpeg_buffer_get_type(); vips_foreign_save_jpeg_target_get_type(); vips_foreign_save_jpeg_mime_get_type(); #endif /*HAVE_JPEG*/ #ifdef HAVE_LIBWEBP vips_foreign_load_webp_file_get_type(); vips_foreign_load_webp_buffer_get_type(); vips_foreign_load_webp_source_get_type(); vips_foreign_save_webp_file_get_type(); vips_foreign_save_webp_buffer_get_type(); vips_foreign_save_webp_target_get_type(); #endif /*HAVE_LIBWEBP*/ #ifdef HAVE_TIFF vips_foreign_load_tiff_file_get_type(); vips_foreign_load_tiff_buffer_get_type(); vips_foreign_load_tiff_source_get_type(); vips_foreign_save_tiff_file_get_type(); vips_foreign_save_tiff_buffer_get_type(); vips_foreign_save_tiff_target_get_type(); #endif /*HAVE_TIFF*/ #if defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE) vips_foreign_load_openslide_file_get_type(); vips_foreign_load_openslide_source_get_type(); #endif /*defined(HAVE_OPENSLIDE) && !defined(OPENSLIDE_MODULE)*/ #if defined(ENABLE_MAGICKLOAD) && !defined(MAGICK_MODULE) #ifdef HAVE_MAGICK6 vips_foreign_load_magick_file_get_type(); vips_foreign_load_magick_buffer_get_type(); #endif /*HAVE_MAGICK6*/ #ifdef HAVE_MAGICK7 vips_foreign_load_magick7_file_get_type(); vips_foreign_load_magick7_buffer_get_type(); #endif /*HAVE_MAGICK7*/ #endif /*defined(ENABLE_MAGICKLOAD) && !defined(MAGICK_MODULE)*/ #if defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE) vips_foreign_save_magick_file_get_type(); vips_foreign_save_magick_buffer_get_type(); vips_foreign_save_magick_bmp_file_get_type(); vips_foreign_save_magick_bmp_buffer_get_type(); vips_foreign_save_magick_gif_file_get_type(); vips_foreign_save_magick_gif_buffer_get_type(); #endif /*defined(ENABLE_MAGICKSAVE) && !defined(MAGICK_MODULE)*/ #ifdef HAVE_CFITSIO vips_foreign_load_fits_file_get_type(); vips_foreign_load_fits_source_get_type(); vips_foreign_save_fits_get_type(); #endif /*HAVE_CFITSIO*/ #ifdef HAVE_OPENEXR vips_foreign_load_openexr_get_type(); #endif /*HAVE_OPENEXR*/ #ifdef HAVE_NIFTI vips_foreign_load_nifti_file_get_type(); vips_foreign_load_nifti_source_get_type(); vips_foreign_save_nifti_get_type(); #endif /*HAVE_NIFTI*/ #if defined(HAVE_HEIF_DECODER) && !defined(HEIF_MODULE) vips_foreign_load_heif_file_get_type(); vips_foreign_load_heif_buffer_get_type(); vips_foreign_load_heif_source_get_type(); #endif /*defined(HAVE_HEIF_DECODER) && !defined(HEIF_MODULE)*/ #if defined(HAVE_HEIF_ENCODER) && !defined(HEIF_MODULE) vips_foreign_save_heif_file_get_type(); vips_foreign_save_heif_buffer_get_type(); vips_foreign_save_heif_target_get_type(); vips_foreign_save_avif_target_get_type(); #endif /*defined(HAVE_HEIF_ENCODER) && !defined(HEIF_MODULE)*/ vips__foreign_load_operation = g_quark_from_static_string( "vips-foreign-load-operation" ); }