/* get, set and copy image header fields * * 9/7/02 JC * - first version * 7/6/05 * - now reads meta fields too * - cleaned up * - added im_header_exists(), im_header_map() * 1/8/05 * - now im_header_get_type() and im_header_get() rather than * im_header_exists() * 4/1/07 * - removed Hist from standard fields ... now a separate function * 29/8/09 * - im_header_get_type() renamed as im_header_get_typeof() to prevent * confusion with GObject-style type definers * 1/10/09 * - rename as header.c * - gtkdoc comments * 22/3/11 * - rename fields for vips8 * - move to vips_ prefix * 16/7/15 * - auto wrap GString as RefString * 20/10/16 * - return header enums as enums, not ints * - vips_image_get_*() all convert everything to target type if they can * - rename "field" as "name" in docs * 21/11/18 * - get_string will allow G_STRING and REF_STRING * 28/12/18 * - hide deprecated header fields from _map * 17/2/19 * - add vips_image_get_page_height() * 19/6/19 * - add vips_image_get_n_pages() * 20/6/19 * - add vips_image_get/set_array_int() * 31/1/19 * - lock for metadata changes */ /* 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 VIPS_DEBUG #define DEBUG */ #ifdef HAVE_CONFIG_H #include #endif /*HAVE_CONFIG_H*/ #include #include #include #include #define VIPS_DISABLE_DEPRECATION_WARNINGS #include #include #include /* For the vips__image_sizeof_bandformat declaration. */ #if ENABLE_DEPRECATED #include #endif /** * SECTION: header * @short_description: get, set and walk image headers * @stability: Stable * @see_also: type * @include: vips/vips.h * * These functions let you get at image header data (including metadata) in a * uniform way. * * Use vips_image_get_typeof() to test for the * existence and #GType of a header field. * * You can attach arbitrary metadata to images. Metadata is copied as images * are processed, so all images which used this image as input, directly or * indirectly, will have this same bit of metadata attached to them. Copying * is implemented with reference-counted pointers, so it is efficient, even for * large items of data. This does however mean that metadata items need to be * immutable. Metadata is handy for things like ICC profiles or EXIF data. * * Various convenience functions (eg. vips_image_set_int()) let you easily * attach * simple types like * numbers, strings and memory blocks to images. Use vips_image_map() to loop * over an image's fields, including all metadata. * * Items of metadata are identified by strings. Some strings are reserved, for * example the ICC profile for an image is known by convention as * "icc-profile-data". * * If you save an image in VIPS format, all metadata (with a restriction, see * below) is automatically saved for you in a block of XML at the end of the * file. When you load a VIPS image, the metadata is restored. You can use the * `vipsedit` command-line tool to extract or replace this block of XML. * * VIPS metadata is based on %GValue. See the docs for that system if you want * to do fancy stuff such as defining a new metadata type. * VIPS defines a new %GValue called `vips_save_string`, a variety of string, * see vips_value_set_save_string(). * If your %GValue can be transformed to `vips_save_string`, it will be * saved and loaded to and from VIPS files for you. * * VIPS provides a couple of base classes which implement * reference-counted areas of memory. If you base your metadata on one of * these types, it can be copied between images efficiently. */ /* Use in various small places where we need a mutex and it's not worth * making a private one. */ static GMutex *vips__meta_lock = NULL; /* We have to keep the gtype as a string, since we statically init this. */ typedef struct _HeaderField { const char *name; const char *type; glong offset; } HeaderField; /* Built in fields and struct offsets. */ static HeaderField vips_header_fields[] = { { "width", "gint", G_STRUCT_OFFSET( VipsImage, Xsize ) }, { "height", "gint", G_STRUCT_OFFSET( VipsImage, Ysize ) }, { "bands", "gint", G_STRUCT_OFFSET( VipsImage, Bands ) }, { "format", "VipsBandFormat", G_STRUCT_OFFSET( VipsImage, BandFmt ) }, { "coding", "VipsCoding", G_STRUCT_OFFSET( VipsImage, Coding ) }, { "interpretation", "VipsInterpretation", G_STRUCT_OFFSET( VipsImage, Type ) }, { "xoffset", "gint", G_STRUCT_OFFSET( VipsImage, Xoffset ) }, { "yoffset", "gint", G_STRUCT_OFFSET( VipsImage, Yoffset ) }, { "xres", "gdouble", G_STRUCT_OFFSET( VipsImage, Xres ) }, { "yres", "gdouble", G_STRUCT_OFFSET( VipsImage, Yres ) }, { "filename", "gchararray", G_STRUCT_OFFSET( VipsImage, filename ) } }; /* Old names we keep around for back-compat. We never loop over these with * map, but we do check them when we look up fields by name. */ static HeaderField vips_header_fields_old[] = { { "Xsize", "gint", G_STRUCT_OFFSET( VipsImage, Xsize ) }, { "Ysize", "gint", G_STRUCT_OFFSET( VipsImage, Ysize ) }, { "Bands", "gint", G_STRUCT_OFFSET( VipsImage, Bands ) }, { "Bbits", "gint", G_STRUCT_OFFSET( VipsImage, Bbits ) }, { "BandFmt", "gint", G_STRUCT_OFFSET( VipsImage, BandFmt ) }, { "Coding", "gint", G_STRUCT_OFFSET( VipsImage, Coding ) }, { "Type", "gint", G_STRUCT_OFFSET( VipsImage, Type ) }, { "Xoffset", "gint", G_STRUCT_OFFSET( VipsImage, Xoffset ) }, { "Yoffset", "gint", G_STRUCT_OFFSET( VipsImage, Yoffset ) }, { "Xres", "gdouble", G_STRUCT_OFFSET( VipsImage, Xres ) }, { "Yres", "gdouble", G_STRUCT_OFFSET( VipsImage, Yres ) } }; /* This is used by (eg.) VIPS_IMAGE_SIZEOF_ELEMENT() to calculate object * size via vips_format_sizeof(). * * It needs to be guint64 and not size_t since we use this as the basis for * image address calcs and they have to be 64-bit, even on 32-bit machines. * * Can't be static, we need this to be visible for vips7 compat. */ const guint64 vips__image_sizeof_bandformat[] = { sizeof( unsigned char ), /* VIPS_FORMAT_UCHAR */ sizeof( signed char ), /* VIPS_FORMAT_CHAR */ sizeof( unsigned short ), /* VIPS_FORMAT_USHORT */ sizeof( unsigned short ), /* VIPS_FORMAT_SHORT */ sizeof( unsigned int ), /* VIPS_FORMAT_UINT */ sizeof( unsigned int ), /* VIPS_FORMAT_INT */ sizeof( float ), /* VIPS_FORMAT_FLOAT */ 2 * sizeof( float ), /* VIPS_FORMAT_COMPLEX */ sizeof( double ), /* VIPS_FORMAT_DOUBLE */ 2 * sizeof( double ) /* VIPS_FORMAT_DPCOMPLEX */ }; /** * vips_format_sizeof: * @format: format type * * Returns: number of bytes for a band format. */ guint64 vips_format_sizeof( VipsBandFormat format ) { format = VIPS_CLIP( 0, format, VIPS_FORMAT_DPCOMPLEX ); return( vips__image_sizeof_bandformat[format] ); } /** * vips_format_sizeof_unsafe: (skip) * @format: format type * * A fast but dangerous version of vips_format_sizeof(). You must have * previously range-checked @format or you'll crash. * * Returns: number of bytes for a band format. */ guint64 vips_format_sizeof_unsafe( VipsBandFormat format ) { g_assert( 0 <= format && format <= VIPS_FORMAT_DPCOMPLEX ); return( vips__image_sizeof_bandformat[format] ); } #ifdef DEBUG /* Check that this meta is on the hash table. */ static void * meta_sanity_on_hash( VipsMeta *meta, VipsImage *im, void *b ) { VipsMeta *found; if( meta->im != im ) printf( "*** field \"%s\" has incorrect im\n", meta->name ); if( !(found = g_hash_table_lookup( im->meta, meta->name )) ) printf( "*** field \"%s\" is on traverse but not in hash\n", meta->name ); if( found != meta ) printf( "*** meta \"%s\" on traverse and hash do not match\n", meta->name ); return( NULL ); } static void meta_sanity_on_traverse( const char *name, VipsMeta *meta, VipsImage *im ) { if( meta->name != name ) printf( "*** field \"%s\" has incorrect name\n", meta->name ); if( meta->im != im ) printf( "*** field \"%s\" has incorrect im\n", meta->name ); if( !g_slist_find( im->meta_traverse, meta ) ) printf( "*** field \"%s\" is in hash but not on traverse\n", meta->name ); } static void meta_sanity( const VipsImage *im ) { if( im->meta ) g_hash_table_foreach( im->meta, (GHFunc) meta_sanity_on_traverse, (void *) im ); vips_slist_map2( im->meta_traverse, (VipsSListMap2Fn) meta_sanity_on_hash, (void *) im, NULL ); } #endif /*DEBUG*/ static void meta_free( VipsMeta *meta ) { #ifdef DEBUG { char *str_value; str_value = g_strdup_value_contents( &meta->value ); printf( "meta_free: name %s, value = %s\n", meta->name, str_value ); g_free( str_value ); } #endif /*DEBUG*/ if( meta->im ) meta->im->meta_traverse = g_slist_remove( meta->im->meta_traverse, meta ); g_value_unset( &meta->value ); g_free( meta->name ); g_free( meta ); } static VipsMeta * meta_new( VipsImage *image, const char *name, GValue *value ) { VipsMeta *meta; meta = g_new( VipsMeta, 1 ); meta->im = image; meta->name = NULL; memset( &meta->value, 0, sizeof( GValue ) ); meta->name = g_strdup( name ); /* Special case: we don't want to have G_STRING on meta. They will be * copied down pipelines, plus some of our API (like * vips_image_get_string()) assumes that the GValue is a refstring and * that read-only pointers can be handed out. * * Turn G_TYPE_STRING into VIPS_TYPE_REF_STRING. */ if( G_VALUE_TYPE( value ) == G_TYPE_STRING ) g_value_init( &meta->value, VIPS_TYPE_REF_STRING ); else g_value_init( &meta->value, G_VALUE_TYPE( value ) ); /* We don't do any conversions that can fail. */ (void) g_value_transform( value, &meta->value ); image->meta_traverse = g_slist_append( image->meta_traverse, meta ); g_hash_table_replace( image->meta, meta->name, meta ); #ifdef DEBUG { char *str_value; str_value = g_strdup_value_contents( value ); printf( "meta_new: name %s, value = %s\n", name, str_value ); g_free( str_value ); } #endif /*DEBUG*/ return( meta ); } /* Destroy all the meta on an image. */ void vips__meta_destroy( VipsImage *image ) { VIPS_FREEF( g_hash_table_destroy, image->meta ); g_assert( !image->meta_traverse ); } static void meta_init( VipsImage *im ) { if( !im->meta ) { g_assert( !im->meta_traverse ); im->meta = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, (GDestroyNotify) meta_free ); } } /** * vips_image_get_width: * @image: image to get from * * Returns: the number of pixels across the image. */ int vips_image_get_width( const VipsImage *image ) { return( image->Xsize ); } /** * vips_image_get_height: * @image: image to get from * * Returns: the number of pixels down the image. */ int vips_image_get_height( const VipsImage *image ) { return( image->Ysize ); } /** * vips_image_get_bands: * @image: image to get from * * Returns: the number of bands (channels) in the image. */ int vips_image_get_bands( const VipsImage *image ) { return( image->Bands ); } /** * vips_image_get_format: * @image: image to get from * * Returns: the format of each band element. */ VipsBandFormat vips_image_get_format( const VipsImage *image ) { return( image->BandFmt ); } /** * vips_image_get_format_max: * @format: the format * * Returns: the maximum numeric value possible for this format. */ double vips_image_get_format_max( VipsBandFormat format ) { switch( format ) { case VIPS_FORMAT_UCHAR: return( UCHAR_MAX ); case VIPS_FORMAT_CHAR: return( SCHAR_MAX ); case VIPS_FORMAT_USHORT: return( USHRT_MAX ); case VIPS_FORMAT_SHORT: return( SHRT_MAX ); case VIPS_FORMAT_UINT: return( UINT_MAX ); case VIPS_FORMAT_INT: return( INT_MAX ); case VIPS_FORMAT_FLOAT: case VIPS_FORMAT_COMPLEX: return( FLT_MAX ); case VIPS_FORMAT_DOUBLE: case VIPS_FORMAT_DPCOMPLEX: return( DBL_MAX ); default: return( -1 ); } } /** * vips_image_guess_format: * @image: image to guess for * * Return the #VipsBandFormat for an image, guessing a sane value if * the set value looks crazy. * * For example, for a float image tagged as rgb16, we'd return ushort. * * Returns: a sensible #VipsBandFormat for the image. */ VipsBandFormat vips_image_guess_format( const VipsImage *image ) { VipsBandFormat format; /* Stop a compiler warning. */ format = VIPS_FORMAT_UCHAR; switch( image->Type ) { case VIPS_INTERPRETATION_B_W: case VIPS_INTERPRETATION_HISTOGRAM: case VIPS_INTERPRETATION_MULTIBAND: format = image->BandFmt; break; case VIPS_INTERPRETATION_FOURIER: if( image->BandFmt == VIPS_FORMAT_DOUBLE || image->BandFmt == VIPS_FORMAT_DPCOMPLEX ) format = VIPS_FORMAT_DPCOMPLEX; else format = VIPS_FORMAT_COMPLEX; break; case VIPS_INTERPRETATION_sRGB: case VIPS_INTERPRETATION_RGB: format = VIPS_FORMAT_UCHAR; break; case VIPS_INTERPRETATION_XYZ: case VIPS_INTERPRETATION_LAB: case VIPS_INTERPRETATION_CMC: case VIPS_INTERPRETATION_LCH: case VIPS_INTERPRETATION_HSV: case VIPS_INTERPRETATION_scRGB: case VIPS_INTERPRETATION_YXY: format = VIPS_FORMAT_FLOAT; break; case VIPS_INTERPRETATION_CMYK: if( image->BandFmt == VIPS_FORMAT_USHORT ) format = VIPS_FORMAT_USHORT; else format = VIPS_FORMAT_UCHAR; break; case VIPS_INTERPRETATION_LABQ: format = VIPS_FORMAT_UCHAR; break; case VIPS_INTERPRETATION_LABS: format = VIPS_FORMAT_SHORT; break; case VIPS_INTERPRETATION_GREY16: case VIPS_INTERPRETATION_RGB16: format = VIPS_FORMAT_USHORT; break; case VIPS_INTERPRETATION_MATRIX: if( image->BandFmt == VIPS_FORMAT_DOUBLE ) format = VIPS_FORMAT_DOUBLE; else format = VIPS_FORMAT_FLOAT; break; default: format = VIPS_FORMAT_NOTSET; break; } return( format ); } /** * vips_image_get_coding: * @image: image to get from * * Returns: the image coding */ VipsCoding vips_image_get_coding( const VipsImage *image ) { return( image->Coding ); } /** * vips_image_get_interpretation: * @image: image to get from * * Return the #VipsInterpretation set in the image header. * Use vips_image_guess_interpretation() if you want a sanity-checked value. * * Returns: the #VipsInterpretation from the image header. */ VipsInterpretation vips_image_get_interpretation( const VipsImage *image ) { return( image->Type ); } /* Try to guess a sane value for interpretation. */ static VipsInterpretation vips_image_default_interpretation( const VipsImage *image ) { switch( image->Coding ) { case VIPS_CODING_LABQ: return( VIPS_INTERPRETATION_LABQ ); case VIPS_CODING_RAD: return( VIPS_INTERPRETATION_sRGB ); default: break; } switch( image->BandFmt ) { case VIPS_FORMAT_UCHAR: case VIPS_FORMAT_SHORT: case VIPS_FORMAT_UINT: case VIPS_FORMAT_INT: switch( image->Bands ) { case 1: case 2: return( VIPS_INTERPRETATION_B_W ); case 3: case 4: return( VIPS_INTERPRETATION_sRGB ); default: return( VIPS_INTERPRETATION_MULTIBAND ); } case VIPS_FORMAT_CHAR: switch( image->Bands ) { case 1: return( VIPS_INTERPRETATION_MATRIX ); default: return( VIPS_INTERPRETATION_MULTIBAND ); } case VIPS_FORMAT_USHORT: switch( image->Bands ) { case 1: case 2: return( VIPS_INTERPRETATION_GREY16 ); case 3: case 4: return( VIPS_INTERPRETATION_RGB16 ); default: return( VIPS_INTERPRETATION_MULTIBAND ); } case VIPS_FORMAT_FLOAT: case VIPS_FORMAT_DOUBLE: switch( image->Bands ) { case 1: case 2: return( VIPS_INTERPRETATION_B_W ); case 3: case 4: return( VIPS_INTERPRETATION_scRGB ); default: return( VIPS_INTERPRETATION_MULTIBAND ); } case VIPS_FORMAT_COMPLEX: case VIPS_FORMAT_DPCOMPLEX: return( VIPS_INTERPRETATION_FOURIER ); default: return( VIPS_INTERPRETATION_MULTIBAND ); } } /** * vips_image_guess_interpretation: * @image: image to guess for * * Return the #VipsInterpretation for an image, guessing a sane value if * the set value looks crazy. * * Returns: a sensible #VipsInterpretation for the image. */ VipsInterpretation vips_image_guess_interpretation( const VipsImage *image ) { gboolean sane; sane = TRUE; /* Coding overrides interpretation. */ switch( image->Coding ) { case VIPS_CODING_ERROR: sane = FALSE; break; case VIPS_CODING_LABQ: if( image->Type != VIPS_INTERPRETATION_LABQ ) sane = FALSE; break; case VIPS_CODING_RAD: if( image->Type != VIPS_INTERPRETATION_sRGB ) sane = FALSE; break; default: break; } switch( image->Type ) { case VIPS_INTERPRETATION_ERROR: sane = FALSE; break; case VIPS_INTERPRETATION_MULTIBAND: /* This is a pretty useless generic tag. Always reset it. */ sane = FALSE; break; case VIPS_INTERPRETATION_B_W: /* Don't test bands, we allow bands after the first to be * unused extras, like alpha. */ break; case VIPS_INTERPRETATION_HISTOGRAM: if( image->Xsize > 1 && image->Ysize > 1 ) sane = FALSE; break; case VIPS_INTERPRETATION_FOURIER: if( !vips_band_format_iscomplex( image->BandFmt ) ) sane = FALSE; break; case VIPS_INTERPRETATION_XYZ: case VIPS_INTERPRETATION_LAB: case VIPS_INTERPRETATION_RGB: case VIPS_INTERPRETATION_CMC: case VIPS_INTERPRETATION_LCH: case VIPS_INTERPRETATION_sRGB: case VIPS_INTERPRETATION_HSV: if( image->Bands < 3 ) sane = FALSE; break; case VIPS_INTERPRETATION_scRGB: case VIPS_INTERPRETATION_YXY: /* Need float values in 0 - 1. */ if( !vips_band_format_isfloat( image->BandFmt ) || image->Bands < 3 ) sane = FALSE; break; case VIPS_INTERPRETATION_CMYK: if( image->Bands < 4 ) sane = FALSE; break; case VIPS_INTERPRETATION_LABQ: if( image->Coding != VIPS_CODING_LABQ ) sane = FALSE; break; case VIPS_INTERPRETATION_LABS: /* Needs to be able to express +/- 32767 */ if( vips_band_format_isuint( image->BandFmt ) || vips_band_format_is8bit( image->BandFmt ) || image->Bands < 3 ) sane = FALSE; break; case VIPS_INTERPRETATION_RGB16: if( vips_band_format_is8bit( image->BandFmt ) || image->Bands < 3 ) sane = FALSE; break; case VIPS_INTERPRETATION_GREY16: if( vips_band_format_is8bit( image->BandFmt ) ) sane = FALSE; break; case VIPS_INTERPRETATION_MATRIX: if( image->Bands != 1 ) sane = FALSE; break; default: g_assert_not_reached(); } if( sane ) return( vips_image_get_interpretation( image ) ); else return( vips_image_default_interpretation( image ) ); } /** * vips_image_get_xres: * @image: image to get from * * Returns: the horizontal image resolution in pixels per millimeter. */ double vips_image_get_xres( const VipsImage *image ) { return( image->Xres ); } /** * vips_image_get_yres: * @image: image to get from * * Returns: the vertical image resolution in pixels per millimeter. */ double vips_image_get_yres( const VipsImage *image ) { return( image->Yres ); } /** * vips_image_get_xoffset: * @image: image to get from * * Returns: the horizontal position of the image origin, in pixels. */ int vips_image_get_xoffset( const VipsImage *image ) { return( image->Xoffset ); } /** * vips_image_get_yoffset: * @image: image to get from * * Returns: the vertical position of the image origin, in pixels. */ int vips_image_get_yoffset( const VipsImage *image ) { return( image->Yoffset ); } /** * vips_image_get_filename: * @image: image to get from * * Returns: the name of the file the image was loaded from, or NULL if there * is no filename. */ const char * vips_image_get_filename( const VipsImage *image ) { return( image->filename ); } /** * vips_image_get_mode: * @image: image to get from * * Image modes are things like `"t"`, meaning a memory buffer, and `"p"` * meaning a delayed computation. * * Returns: the image mode. */ const char * vips_image_get_mode( const VipsImage *image ) { return( image->mode ); } /** * vips_image_get_scale: * @image: image to get from * * Matrix images can have an optional `scale` field for use by integer * convolution. * * Returns: the scale. */ double vips_image_get_scale( const VipsImage *image ) { double scale; scale = 1.0; if( vips_image_get_typeof( image, "scale" ) ) vips_image_get_double( image, "scale", &scale ); return( scale ); } /** * vips_image_get_offset: * @image: image to get from * * Matrix images can have an optional `offset` field for use by integer * convolution. * * Returns: the offset. */ double vips_image_get_offset( const VipsImage *image ) { double offset; offset = 0.0; if( vips_image_get_typeof( image, "offset" ) ) vips_image_get_double( image, "offset", &offset ); return( offset ); } /** * vips_image_get_page_height: * @image: image to get from * * Multi-page images can have a page height. Fetch it, and sanity check it. If * page-height is not set, it defaults to the image height. * * Returns: the page height. */ int vips_image_get_page_height( VipsImage *image ) { int page_height; if( vips_image_get_typeof( image, VIPS_META_PAGE_HEIGHT ) && !vips_image_get_int( image, VIPS_META_PAGE_HEIGHT, &page_height ) && page_height > 0 && page_height < image->Ysize && image->Ysize % page_height == 0 ) return( page_height ); return( image->Ysize ); } /** * vips_image_get_n_pages: * @image: image to get from * * Fetch and sanity-check #VIPS_META_N_PAGES. Default to 1 if not present or * crazy. * * This is the number of pages in the image file, not the number of pages that * have been loaded into @image. * * Returns: the number of pages in the image file */ int vips_image_get_n_pages( VipsImage *image ) { int n_pages; if( vips_image_get_typeof( image, VIPS_META_N_PAGES ) && !vips_image_get_int( image, VIPS_META_N_PAGES, &n_pages ) && n_pages > 1 && n_pages < 10000 ) return( n_pages ); return( 1 ); } /** * vips_image_get_n_subifds: * @image: image to get from * * Fetch and sanity-check #VIPS_META_N_SUBIFDS. Default to 0 if not present or * crazy. * * Returns: the number of subifds in the image file */ int vips_image_get_n_subifds( VipsImage *image ) { int n_subifds; if( vips_image_get_typeof( image, VIPS_META_N_SUBIFDS ) && !vips_image_get_int( image, VIPS_META_N_SUBIFDS, &n_subifds ) && n_subifds > 1 && n_subifds < 1000 ) return( n_subifds ); return( 0 ); } /** * vips_image_get_orientation: * @image: image to get from * * Fetch and sanity-check #VIPS_META_ORIENTATION. Default to 1 (no rotate, * no flip) if not present or crazy. * * Returns: the image orientation. */ int vips_image_get_orientation( VipsImage *image ) { int orientation; if( vips_image_get_typeof( image, VIPS_META_ORIENTATION ) && !vips_image_get_int( image, VIPS_META_ORIENTATION, &orientation ) && orientation > 0 && orientation < 9 ) return( orientation ); return( 1 ); } /** * vips_image_get_orientation_swap: * @image: image to get from * * Return %TRUE if applying the orientation would swap width and height. * * Returns: if width/height will swap */ gboolean vips_image_get_orientation_swap( VipsImage *image ) { int orientation = vips_image_get_orientation( image ); return( orientation >= 5 && orientation <= 8 ); } /** * vips_image_get_data: * @image: image to get data for * * Return a pointer to the image's pixel data, if possible. This can involve * allocating large amounts of memory and performing a long computation. Image * pixels are laid out in band-packed rows. * * Since this function modifies @image, it is not threadsafe. Only call it on * images which you are sure have not been shared with another thread. * * See also: vips_image_wio_input(), vips_image_copy_memory(). * * Returns: (transfer none): a pointer to pixel data, if possible. */ const void * vips_image_get_data( VipsImage *image ) { if( vips_image_wio_input( image ) ) return( NULL ); return( image->data ); } /** * vips_image_init_fields: * @image: image to init * @xsize: image width * @ysize: image height * @bands: image bands * @format: band format * @coding: image coding * @interpretation: image type * @xres: horizontal resolution, pixels per millimetre * @yres: vertical resolution, pixels per millimetre * * A convenience function to set the header fields after creating an image. * Normally you copy the fields from your input images with * vips_image_pipelinev() and then make * any adjustments you need, but if you are creating an image from scratch, * for example vips_black() or vips_jpegload(), you do need to set all the * fields yourself. * * See also: vips_image_pipelinev(). */ void vips_image_init_fields( VipsImage *image, int xsize, int ysize, int bands, VipsBandFormat format, VipsCoding coding, VipsInterpretation interpretation, double xres, double yres ) { g_object_set( image, "width", xsize, "height", ysize, "bands", bands, "format", format, NULL ); image->Coding = coding; image->Type = interpretation; image->Xres = VIPS_MAX( 0, xres ); image->Yres = VIPS_MAX( 0, yres ); } static void * meta_cp_field( VipsMeta *meta, VipsImage *dst, void *b ) { #ifdef DEBUG { char *str_value; str_value = g_strdup_value_contents( &meta->value ); printf( "vips__meta_cp: copying name %s, value = %s\n", meta->name, str_value ); g_free( str_value ); } #endif /*DEBUG*/ (void) meta_new( dst, meta->name, &meta->value ); #ifdef DEBUG meta_sanity( dst ); #endif /*DEBUG*/ return( NULL ); } /* Copy meta on to dst. */ int vips__image_meta_copy( VipsImage *dst, const VipsImage *src ) { if( src->meta ) { /* We lock with vips_image_set() to stop races in highly- * threaded applications. */ g_mutex_lock( vips__meta_lock ); meta_init( dst ); vips_slist_map2( src->meta_traverse, (VipsSListMap2Fn) meta_cp_field, dst, NULL ); g_mutex_unlock( vips__meta_lock ); } return( 0 ); } /* We have to have this as a separate entry point so we can support the old * vips7 API. */ int vips__image_copy_fields_array( VipsImage *out, VipsImage *in[] ) { int i; int ni; g_assert( in[0] ); /* Copy magic too, handy for knowing the original image's byte order. */ out->magic = in[0]->magic; out->Xsize = in[0]->Xsize; out->Ysize = in[0]->Ysize; out->Bands = in[0]->Bands; out->Bbits = in[0]->Bbits; out->BandFmt = in[0]->BandFmt; out->Type = in[0]->Type; out->Coding = in[0]->Coding; out->Xres = in[0]->Xres; out->Yres = in[0]->Yres; out->Xoffset = in[0]->Xoffset; out->Yoffset = in[0]->Yoffset; /* Count number of images. */ for( ni = 0; in[ni]; ni++ ) ; /* Need to copy last-to-first so that in0 meta will override any * earlier meta. * * Don't destroy the meta on out. Things like foreign.c like setting * image properties before calling a subclass loader, and those * subclass loaders will sometimes write to an image. */ for( i = ni - 1; i >= 0; i-- ) if( vips__image_meta_copy( out, in[i] ) ) return( -1 ); /* Merge hists first to last. */ for( i = 0; in[i]; i++ ) out->history_list = vips__gslist_gvalue_merge( out->history_list, in[i]->history_list ); return( 0 ); } /** * vips_image_set: * @image: image to set the metadata on * @name: the name to give the metadata * @value: the %GValue to copy into the image * * Set a piece of metadata on @image. Any old metadata with that name is * destroyed. The %GValue is copied into the image, so you need to unset the * value when you're done with it. * * For example, to set an integer on an image (though you would use the * convenience function vips_image_set_int() in practice), you would do: * * |[ * GValue value = { 0 }; * * g_value_init (&value, G_TYPE_INT); * g_value_set_int (&value, 42); * vips_image_set (image, name, &value); * g_value_unset (&value); * ]| * * See also: vips_image_get(). */ void vips_image_set( VipsImage *image, const char *name, GValue *value ) { g_assert( name ); g_assert( value ); /* We lock between modifying metadata and copying metadata between * images, see vips__image_meta_copy(). * * This prevents modification of metadata by one thread racing with * metadata copy on another -- this can lead to crashes in * highly-threaded applications. */ g_mutex_lock( vips__meta_lock ); meta_init( image ); (void) meta_new( image, name, value ); g_mutex_unlock( vips__meta_lock ); /* If we're setting an EXIF data block, we need to automatically expand * out all the tags. This will set things like xres/yres too. * * We do this here rather than in meta_new() since we don't want to * trigger on copy_fields. */ if( strcmp( name, VIPS_META_EXIF_NAME ) == 0 ) if( vips__exif_parse( image ) ) g_warning( "image_set: bad exif data" ); #ifdef DEBUG meta_sanity( image ); #endif /*DEBUG*/ } /* Unforunately gvalue seems to have no way of doing this. Just handle the vips * built-in types. */ static void vips_set_value_from_pointer( GValue *value, void *data ) { GType type = G_VALUE_TYPE( value ); /* The fundamental type ... eg. G_TYPE_ENUM for a VIPS_TYPE_KERNEL, * or G_TYPE_OBJECT for VIPS_TYPE_IMAGE(). */ GType fundamental = G_TYPE_FUNDAMENTAL( type ); if( fundamental == G_TYPE_INT ) g_value_set_int( value, *((int *) data) ); else if( fundamental == G_TYPE_DOUBLE ) g_value_set_double( value, *((double *) data) ); else if( fundamental == G_TYPE_ENUM ) g_value_set_enum( value, *((int *) data) ); else if( fundamental == G_TYPE_STRING ) g_value_set_string( value, *((char **) data) ); else g_warning( "%s: unimplemented vips_set_value_from_pointer() " "type %s", G_STRLOC, g_type_name( type ) ); } /** * vips_image_get: * @image: image to get the field from from * @name: the name to fetch * @value_copy: (transfer full) (out caller-allocates): the %GValue is copied into this * * Fill @value_copy with a copy of the header field. @value_copy must be zeroed * but uninitialised. * * This will return -1 and add a message to the error buffer if the field * does not exist. Use vips_image_get_typeof() to test for the * existence of a field first if you are not certain it will be there. * * For example, to read a double from an image (though of course you would use * vips_image_get_double() in practice): * * |[ * GValue value = { 0 }; * double d; * * if (vips_image_get (image, name, &value)) * return -1; * * if (G_VALUE_TYPE (&value) != G_TYPE_DOUBLE) { * vips_error( "mydomain", * _("field \"%s\" is of type %s, not double"), * name, * g_type_name (G_VALUE_TYPE (&value))); * g_value_unset (&value); * return -1; * } * * d = g_value_get_double (&value); * g_value_unset (&value); * ]| * * See also: vips_image_get_typeof(), vips_image_get_double(). * * Returns: (skip): 0 on success, -1 otherwise. */ int vips_image_get( const VipsImage *image, const char *name, GValue *value_copy ) { int i; VipsMeta *meta; g_assert( name ); g_assert( value_copy ); for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) { HeaderField *field = &vips_header_fields[i]; if( strcmp( field->name, name ) == 0 ) { GType gtype = g_type_from_name( field->type ); g_value_init( value_copy, gtype ); vips_set_value_from_pointer( value_copy, G_STRUCT_MEMBER_P( image, field->offset ) ); return( 0 ); } } for( i = 0; i < VIPS_NUMBER( vips_header_fields_old ); i++ ) { HeaderField *field = &vips_header_fields_old[i]; if( strcmp( field->name, name ) == 0 ) { GType gtype = g_type_from_name( field->type ); g_value_init( value_copy, gtype ); vips_set_value_from_pointer( value_copy, G_STRUCT_MEMBER_P( image, field->offset ) ); return( 0 ); } } if( image->meta && (meta = g_hash_table_lookup( image->meta, name )) ) { g_value_init( value_copy, G_VALUE_TYPE( &meta->value ) ); g_value_copy( &meta->value, value_copy ); return( 0 ); } vips_error( "vips_image_get", _( "field \"%s\" not found" ), name ); return( -1 ); } /** * vips_image_get_typeof: * @image: image to test * @name: the name to search for * * Read the %GType for a header field. Returns zero if there is no * field of that name. * * See also: vips_image_get(). * * Returns: the %GType of the field, or zero if there is no * field of that name. */ GType vips_image_get_typeof( const VipsImage *image, const char *name ) { int i; VipsMeta *meta; g_assert( name ); for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) { HeaderField *field = &vips_header_fields[i]; if( strcmp( field->name, name ) == 0 ) return( g_type_from_name( field->type ) ); } for( i = 0; i < VIPS_NUMBER( vips_header_fields_old ); i++ ) { HeaderField *field = &vips_header_fields_old[i]; if( strcmp( field->name, name ) == 0 ) return( g_type_from_name( field->type ) ); } if( image->meta && (meta = g_hash_table_lookup( image->meta, name )) ) return( G_VALUE_TYPE( &meta->value ) ); VIPS_DEBUG_MSG( "vips_image_get_typeof: unknown field %s\n", name ); return( 0 ); } /** * vips_image_remove: * @image: image to test * @name: the name to search for * * Find and remove an item of metadata. Return %FALSE if no metadata of that * name was found. * * See also: vips_image_set(), vips_image_get_typeof(). * * Returns: %TRUE if an item of metadata of that name was found and removed */ gboolean vips_image_remove( VipsImage *image, const char *name ) { gboolean result; result = FALSE; if( image->meta ) { /* We lock between modifying metadata and copying metadata * between images, see vips__image_meta_copy(). * * This prevents modification of metadata by one thread * racing with metadata copy on another -- this can lead to * crashes in highly-threaded applications. */ g_mutex_lock( vips__meta_lock ); result = g_hash_table_remove( image->meta, name ); g_mutex_unlock( vips__meta_lock ); } return( result ); } /* Deprecated header fields we hide from _map. */ static const char *vips_image_header_deprecated[] = { "ipct-data", "gif-delay", "gif-loop" }; static void * vips_image_map_fn( VipsMeta *meta, VipsImageMapFn fn, void *a ) { int i; /* Hide deprecated fields. */ for( i = 0; i < VIPS_NUMBER( vips_image_header_deprecated ); i++ ) if( strcmp( meta->name, vips_image_header_deprecated[i] ) == 0 ) return( NULL ); return( fn( meta->im, meta->name, &meta->value, a ) ); } /** * vips_image_map: * @image: image to map over * @fn: (scope call): function to call for each header field * @a: (closure fn): user data for function * * This function calls @fn for every header field, including every item of * metadata. * * Like all _map functions, the user function should return %NULL to continue * iteration, or a non-%NULL pointer to indicate early termination. * * See also: vips_image_get_typeof(), vips_image_get(). * * Returns: (transfer none): %NULL on success, the failing pointer otherwise. */ void * vips_image_map( VipsImage *image, VipsImageMapFn fn, void *a ) { int i; GValue value = { 0 }; void *result; for( i = 0; i < VIPS_NUMBER( vips_header_fields ); i++ ) { HeaderField *field = &vips_header_fields[i]; (void) vips_image_get( image, field->name, &value ); result = fn( image, field->name, &value, a ); g_value_unset( &value ); if( result ) return( result ); } if( image->meta_traverse && (result = vips_slist_map2( image->meta_traverse, (VipsSListMap2Fn) vips_image_map_fn, fn, a )) ) return( result ); return( NULL ); } static void * count_fields( VipsImage *image, const char *field, GValue *value, void *a ) { int *n_fields = (int *) a; *n_fields += 1; return( NULL ); } static void * add_fields( VipsImage *image, const char *field, GValue *value, void *a ) { gchar ***p = (gchar ***) a; **p = g_strdup( field ); *p += 1; return( NULL ); } /** * vips_image_get_fields: * @image: image to get fields from * * Get a %NULL-terminated array listing all the metadata field names on @image. * Free the return result with g_strfreev(). * * This is handy for language bindings. From C, it's usually more convenient to * use vips_image_map(). * * Returns: (transfer full): metadata fields in image, as a %NULL-terminated * array. */ gchar ** vips_image_get_fields( VipsImage *image ) { int n_fields; gchar **fields; gchar **p; n_fields = 0; (void) vips_image_map( image, count_fields, &n_fields ); fields = g_new0( gchar *, n_fields + 1 ); p = fields; (void) vips_image_map( image, add_fields, &p ); return( fields ); } /** * vips_image_set_area: * @image: image to attach the metadata to * @name: metadata name * @free_fn: (scope async): free function for @data * @data: pointer to area of memory * * Attaches @data as a metadata item on @image under the name @name. When * VIPS no longer needs the metadata, it will be freed with @free_fn. * * See also: vips_image_get_double(), vips_image_set() */ void vips_image_set_area( VipsImage *image, const char *name, VipsCallbackFn free_fn, void *data ) { GValue value = { 0 }; vips_value_set_area( &value, free_fn, data ); vips_image_set( image, name, &value ); g_value_unset( &value ); } static int meta_get_value( const VipsImage *image, const char *name, GType type, GValue *value_copy ) { GValue value = { 0 }; if( vips_image_get( image, name, &value ) ) return( -1 ); g_value_init( value_copy, type ); if( !g_value_transform( &value, value_copy ) ) { vips_error( "VipsImage", _( "field \"%s\" is of type %s, not %s" ), name, g_type_name( G_VALUE_TYPE( &value ) ), g_type_name( type ) ); g_value_unset( &value ); return( -1 ); } g_value_unset( &value ); return( 0 ); } /** * vips_image_get_area: * @image: image to get the metadata from * @name: metadata name * @data: (out): return metadata value * * Gets @data from @image under the name @name. A convenience * function over vips_image_get(). Use vips_image_get_typeof() to test for * the existence of a piece of metadata. * * See also: vips_image_set_area(), vips_image_get(), * vips_image_get_typeof() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_area( const VipsImage *image, const char *name, const void **data ) { GValue value_copy = { 0 }; if( !meta_get_value( image, name, VIPS_TYPE_AREA, &value_copy ) ) { *data = vips_value_get_area( &value_copy, NULL ); g_value_unset( &value_copy ); return( 0 ); } return( -1 ); } /** * vips_image_set_blob: * @image: image to attach the metadata to * @name: metadata name * @free_fn: (scope async): free function for @data * @data: (array length=length) (element-type guint8): pointer to area of memory * @length: length of memory area * * Attaches @blob as a metadata item on @image under the name @name. A * convenience * function over vips_image_set() using a vips_blob. * * See also: vips_image_get_blob(), vips_image_set(). */ void vips_image_set_blob( VipsImage *image, const char *name, VipsCallbackFn free_fn, const void *data, size_t size ) { GValue value = { 0 }; g_value_init( &value, VIPS_TYPE_BLOB ); vips_value_set_blob( &value, free_fn, data, size ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_set_blob_copy: * @image: image to attach the metadata to * @name: metadata name * @data: (array length=length) (element-type guint8): pointer to area of memory * @length: length of memory area * * Attaches @blob as a metadata item on @image under the name @name, taking * a copy of the memory area. A convenience function over * vips_image_set_blob(). * * See also: vips_image_get_blob(), vips_image_set(). */ void vips_image_set_blob_copy( VipsImage *image, const char *name, const void *data, size_t length ) { void *data_copy; /* Cap at 100mb for sanity. */ if( !data || length == 0 || length > 100 * 1024 * 1024 ) return; /* We add an extra, secret null byte at the end, just in case this blob * is read as a C string. The libtiff reader attaches * XMP XML as a blob, for example. */ if( !(data_copy = vips_malloc( NULL, length + 1 )) ) return; memcpy( data_copy, data, length ); ((unsigned char *) data_copy)[length] = '\0'; vips_image_set_blob( image, name, (VipsCallbackFn) vips_area_free_cb, data_copy, length ); } /** * vips_image_get_blob: * @image: image to get the metadata from * @name: metadata name * @data: (out) (array length=length) (element-type guint8): pointer to area of memory * @length: (out): return the blob length here, optionally * * Gets @blob from @image under the name @name, optionally returns its length in * @length. A convenience * function over vips_image_get(). Use vips_image_get_typeof() to test for the * existence * of a piece of metadata. * * See also: vips_image_get(), vips_image_get_typeof(), vips_blob_get(), * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_blob( const VipsImage *image, const char *name, const void **data, size_t *length ) { GValue value_copy = { 0 }; if( !meta_get_value( image, name, VIPS_TYPE_BLOB, &value_copy ) ) { *data = vips_value_get_blob( &value_copy, length ); g_value_unset( &value_copy ); return( 0 ); } return( -1 ); } /** * vips_image_get_int: * @image: image to get the header field from * @name: field name * @out: (out): return field value * * Gets @out from @im under the name @name. * The value will be transformed into * an int, if possible. * * See also: vips_image_get(), vips_image_get_typeof() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_int( const VipsImage *image, const char *name, int *out ) { GValue value = { 0 }; if( meta_get_value( image, name, G_TYPE_INT, &value ) ) return( -1 ); *out = g_value_get_int( &value ); g_value_unset( &value ); return( 0 ); } /** * vips_image_set_int: * @image: image to attach the metadata to * @name: metadata name * @i: metadata value * * Attaches @i as a metadata item on @image under the name @name. A * convenience * function over vips_image_set(). * * See also: vips_image_get_int(), vips_image_set() */ void vips_image_set_int( VipsImage *image, const char *name, int i ) { GValue value = { 0 }; g_value_init( &value, G_TYPE_INT ); g_value_set_int( &value, i ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_get_double: * @image: image to get the header field from * @name: field name * @out: (out): return field value * * Gets @out from @im under the name @name. * The value will be transformed into * a double, if possible. * * See also: vips_image_get(), vips_image_get_typeof() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_double( const VipsImage *image, const char *name, double *out ) { GValue value = { 0 }; if( meta_get_value( image, name, G_TYPE_DOUBLE, &value ) ) return( -1 ); *out = g_value_get_double( &value ); g_value_unset( &value ); return( 0 ); } /** * vips_image_set_double: * @image: image to attach the metadata to * @name: metadata name * @d: metadata value * * Attaches @d as a metadata item on @image as @name. A * convenience * function over vips_image_set(). * * See also: vips_image_get_double(), vips_image_set() */ void vips_image_set_double( VipsImage *image, const char *name, double d ) { GValue value = { 0 }; g_value_init( &value, G_TYPE_DOUBLE ); g_value_set_double( &value, d ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_get_string: * @image: image to get the header field from * @name: field name * @out: (out) (transfer none): return field value * * Gets @out from @im under the name @name. * The field must be of type * G_TYPE_STRING, VIPS_TYPE_REF_STRING. * * Do not free @out. * * Use vips_image_get_as_string() to fetch any field as a string. * * See also: vips_image_get(), vips_image_get_typeof() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_string( const VipsImage *image, const char *name, const char **out ) { GValue value = { 0 }; if( vips_image_get( image, name, &value ) ) return( -1 ); if( G_VALUE_TYPE( &value ) == VIPS_TYPE_REF_STRING ) { VipsArea *area; area = g_value_get_boxed( &value ); *out = area->data; } else if( G_VALUE_TYPE( &value ) == G_TYPE_STRING ) { *out = g_value_get_string( &value ); } else { vips_error( "VipsImage", _( "field \"%s\" is of type %s, not VipsRefString" ), name, g_type_name( G_VALUE_TYPE( &value ) ) ); g_value_unset( &value ); return( -1 ); } g_value_unset( &value ); return( 0 ); } /** * vips_image_set_string: * @image: image to attach the metadata to * @name: metadata name * @str: metadata value * * Attaches @str as a metadata item on @image as @name. * A convenience * function over vips_image_set() using #VIPS_TYPE_REF_STRING. * * See also: vips_image_get_double(), vips_image_set(). */ void vips_image_set_string( VipsImage *image, const char *name, const char *str ) { GValue value = { 0 }; g_value_init( &value, VIPS_TYPE_REF_STRING ); vips_value_set_ref_string( &value, str ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_get_as_string: * @image: image to get the header field from * @name: field name * @out: (out) (transfer full): return field value as string * * Returns @name from @image in @out. * This function will read any field, returning it as a printable string. * You need to free the string with g_free() when you are done with it. * * This will base64-encode BLOBs, for example. Use vips_buf_appendgv() to * make a string that's for humans. * * See also: vips_image_get(), vips_image_get_typeof(), vips_buf_appendgv(). * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_as_string( const VipsImage *image, const char *name, char **out ) { GValue value = { 0 }; GType type; if( vips_image_get( image, name, &value ) ) return( -1 ); /* Display the save form, if there is one. This way we display * something useful for ICC profiles, xml fields, etc. */ type = G_VALUE_TYPE( &value ); if( g_value_type_transformable( type, VIPS_TYPE_SAVE_STRING ) ) { GValue save_value = { 0 }; g_value_init( &save_value, VIPS_TYPE_SAVE_STRING ); if( !g_value_transform( &value, &save_value ) ) return( -1 ); *out = g_strdup( vips_value_get_save_string( &save_value ) ); g_value_unset( &save_value ); } else *out = g_strdup_value_contents( &value ); g_value_unset( &value ); return( 0 ); } /** * vips_image_print_field: * @image: image to get the header field from * @name: field name * * Prints field @name to stdout as ASCII. Handy for debugging. */ void vips_image_print_field( const VipsImage *image, const char *name ) { char *str; if( vips_image_get_as_string( image, name, &str ) ) { printf( "vips_image_print_field: unable to read field\n" ); return; } printf( ".%s: %s\n", name, str ); g_free( str ); } /** * vips_image_get_image: * @image: image to get the metadata from * @name: metadata name * @out: (out) (transfer full): return metadata value * * Gets @out from @im under the name @name. * The field must be of type * #VIPS_TYPE_IMAGE. You must unref @out with g_object_unref(). * * Use vips_image_get_typeof() to test for the * existence of a piece of metadata. * * See also: vips_image_get(), vips_image_set_image() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_image( const VipsImage *image, const char *name, VipsImage **out ) { GValue value = { 0 }; if( meta_get_value( image, name, VIPS_TYPE_IMAGE, &value ) ) return( -1 ); *out = g_value_dup_object( &value ); g_value_unset( &value ); return( 0 ); } /** * vips_image_set_image: * @image: image to attach the metadata to * @name: metadata name * @im: metadata value * * Attaches @im as a metadata item on @image as @name. * A convenience function over vips_image_set(). * * See also: vips_image_get_image(), vips_image_set(). */ void vips_image_set_image( VipsImage *image, const char *name, VipsImage *im ) { GValue value = { 0 }; g_value_init( &value, VIPS_TYPE_IMAGE ); g_value_set_object( &value, im ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_get_array_int: * @image: image to get the metadata from * @name: metadata name * @out: (out) (array length=n) (transfer none): return pointer to array * @n: (out) (optional): return the number of elements here, optionally * * Gets @out from @im under the name @name. * The field must be of type * #VIPS_TYPE_ARRAY_INT. * * Do not free @out. @out is valid as long as @image is valid. * * Use vips_image_get_typeof() to test for the * existence of a piece of metadata. * * See also: vips_image_get(), vips_image_set_image() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_array_int( VipsImage *image, const char *name, int **out, int *n ) { GValue value = { 0 }; if( meta_get_value( image, name, VIPS_TYPE_ARRAY_INT, &value ) ) return( -1 ); *out = vips_value_get_array_int( &value, n ); g_value_unset( &value ); return( 0 ); } /** * vips_image_set_array_int: * @image: image to attach the metadata to * @name: metadata name * @array: (array length=n) (allow-none): array of ints * @n: the number of elements * * Attaches @array as a metadata item on @image as @name. * A convenience function over vips_image_set(). * * See also: vips_image_get_image(), vips_image_set(). */ void vips_image_set_array_int( VipsImage *image, const char *name, const int *array, int n ) { GValue value = { 0 }; g_value_init( &value, VIPS_TYPE_ARRAY_INT ); vips_value_set_array_int( &value, array, n ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_get_array_double: * @image: image to get the metadata from * @name: metadata name * @out: (out) (array length=n) (transfer none): return pointer to array * @n: (out) (optional): return the number of elements here, optionally * * Gets @out from @im under the name @name. * The field must be of type * #VIPS_TYPE_ARRAY_INT. * * Do not free @out. @out is valid as long as @image is valid. * * Use vips_image_get_typeof() to test for the * existence of a piece of metadata. * * See also: vips_image_get(), vips_image_set_image() * * Returns: 0 on success, -1 otherwise. */ int vips_image_get_array_double( VipsImage *image, const char *name, double **out, int *n ) { GValue value = { 0 }; if( meta_get_value( image, name, VIPS_TYPE_ARRAY_DOUBLE, &value ) ) return( -1 ); *out = vips_value_get_array_double( &value, n ); g_value_unset( &value ); return( 0 ); } /** * vips_image_set_array_double: * @image: image to attach the metadata to * @name: metadata name * @array: (array length=n) (allow-none): array of doubles * @n: the number of elements * * Attaches @array as a metadata item on @image as @name. * A convenience function over vips_image_set(). * * See also: vips_image_get_image(), vips_image_set(). */ void vips_image_set_array_double( VipsImage *image, const char *name, const double *array, int n ) { GValue value = { 0 }; g_value_init( &value, VIPS_TYPE_ARRAY_DOUBLE ); vips_value_set_array_double( &value, array, n ); vips_image_set( image, name, &value ); g_value_unset( &value ); } /** * vips_image_history_printf: * @image: add history line to this image * @format: printf() format string * @...: arguments to format string * * Add a line to the image history. The @format and arguments are expanded, the * date and time is appended prefixed with a hash character, and the whole * string is appended to the image history and terminated with a newline. * * For example: * * |[ * vips_image_history_printf (image, "vips invert %s %s", * in->filename, out->filename); * ]| * * Might add the string * * |[ * "vips invert /home/john/fred.v /home/john/jim.v # Fri Apr 3 23:30:35 2009\n" * ]| * * VIPS operations don't add history lines for you because a single action at * the application level might involve many VIPS operations. History must be * recorded by the application. * * Returns: 0 on success, -1 on error. */ int vips_image_history_printf( VipsImage *image, const char *fmt, ... ) { va_list args; char str[VIPS_PATH_MAX]; VipsBuf buf = VIPS_BUF_STATIC( str ); time_t timebuf; va_start( args, fmt ); (void) vips_buf_vappendf( &buf, fmt, args ); va_end( args ); vips_buf_appends( &buf, " # " ); /* Add the date. ctime always attaches a '\n', gah. */ time( &timebuf ); vips_buf_appends( &buf, ctime( &timebuf ) ); vips_buf_removec( &buf, '\n' ); #ifdef DEBUG printf( "vips_image_history_printf: " "adding:\n\t%s\nto history on image %p\n", vips_buf_all( &buf ), image ); #endif /*DEBUG*/ image->history_list = g_slist_append( image->history_list, vips__gvalue_ref_string_new( vips_buf_all( &buf ) ) ); return( 0 ); } /** * vips_image_history_args: * @image: image to attach history line to * @name: program name * @argc: number of program arguments * @argv: (array length=argc) (element-type char*): program arguments * * Formats the name/argv as a single string and calls * vips_image_history_printf(). A * convenience function for command-line prorams. * * See also: vips_image_get_history(). * * Returns: 0 on success, -1 on error. */ int vips_image_history_args( VipsImage *image, const char *name, int argc, char *argv[] ) { int i; char txt[1024]; VipsBuf buf = VIPS_BUF_STATIC( txt ); vips_buf_appends( &buf, name ); for( i = 0; i < argc; i++ ) { vips_buf_appends( &buf, " " ); vips_buf_appends( &buf, argv[i] ); } if( vips_image_history_printf( image, "%s", vips_buf_all( &buf ) ) ) return( -1 ); return( 0 ); } /** * vips_image_get_history: * @image: get history from here * * This function reads the image history as a C string. The string is owned * by VIPS and must not be freed. * * VIPS tracks the history of each image, that is, the sequence of operations * that generated that image. Applications built on VIPS need to call * vips_image_history_printf() for each action they perform, setting the * command-line equivalent for the action. * * See also: vips_image_history_printf(). * * Returns: (transfer none): The history of @image as a C string. Do not free! */ const char * vips_image_get_history( VipsImage *image ) { if( !image->Hist ) image->Hist = vips__gslist_gvalue_get( image->history_list ); return( image->Hist ? image->Hist : "" ); } /* Called during vips_init(). */ void vips__meta_init( void ) { if( !vips__meta_lock ) vips__meta_lock = vips_g_mutex_new(); }