From 92784986231c7c81037fde4a36694045a205138d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 2 Jul 2013 12:08:46 +0100 Subject: [PATCH] add vips_matrixload(), vips_matrixsave() read and write vips mat format to and from VipsImage also fix a leak with vips_check_matrix() --- ChangeLog | 1 + TODO | 6 +- libvips/foreign/Makefile.am | 2 + libvips/foreign/csv.c | 381 +++++++++++++++++++++++---------- libvips/foreign/csv.h | 7 + libvips/foreign/foreign.c | 4 + libvips/foreign/matrixload.c | 202 +++++++++++++++++ libvips/foreign/matrixsave.c | 157 ++++++++++++++ libvips/include/vips/foreign.h | 5 + libvips/include/vips/header.h | 23 +- libvips/iofuncs/error.c | 8 +- libvips/iofuncs/header.c | 43 +++- libvips/resample/quadratic.c | 11 + 13 files changed, 713 insertions(+), 137 deletions(-) create mode 100644 libvips/foreign/matrixload.c create mode 100644 libvips/foreign/matrixsave.c diff --git a/ChangeLog b/ChangeLog index 17293d99..d784be3e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,5 @@ 1/7/13 started 7.35.0 +- added vips_matrixload() and vips_matrixsave(), load and save vips mat format 28/6/13 started 7.34.1 - fix morphological operators on non-uchar images diff --git a/TODO b/TODO index aa6d994c..2eed031e 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,6 @@ -- 3-way dilate on a float image seems to be broken +- move im_buildlut() to create package + + others too? identity? - object construction is threadsafe, but class construction is not @@ -37,8 +39,6 @@ because we step across tiles left to right: y doesn't change, only x does -- seems to leak with vips_check_matrix()? - - test with interpolators, do we add margins correctly? diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index bce7ef8d..e332a5da 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -13,6 +13,8 @@ libforeign_la_SOURCES = \ csv.c \ csvload.c \ csvsave.c \ + matrixload.c \ + matrixsave.c \ dzsave.c \ rawload.c \ rawsave.c \ diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index ecb4a3d6..930b0059 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -25,6 +25,8 @@ * - rework as a set of fns ready for wrapping as a class * 23/2/12 * - report positions for EOF/EOL errors + * 2/7/13 + * - add array read/write */ /* @@ -88,7 +90,8 @@ skip_line( FILE *fp ) /* If we hit EOF and no \n, wait until the next call to report EOF. */ - while( (ch = fgetc( fp )) != '\n' && ch != EOF ) + while( (ch = fgetc( fp )) != '\n' && + ch != EOF ) ; return( -1 ); @@ -101,7 +104,9 @@ skip_white( FILE *fp, const char whitemap[256] ) do { ch = fgetc( fp ); - } while (ch != EOF && ch != '\n' && whitemap[ch] ); + } while( ch != EOF && + ch != '\n' && + whitemap[ch] ); ungetc( ch, fp ); @@ -122,7 +127,8 @@ skip_to_quote( FILE *fp ) ch = fgetc( fp ); else if( ch == '"' ) break; - } while( ch != EOF && ch != '\n' ); + } while( ch != EOF && + ch != '\n' ); ungetc( ch, fp ); @@ -136,7 +142,9 @@ skip_to_sep( FILE *fp, const char sepmap[256] ) do { ch = fgetc( fp ); - } while (ch != EOF && ch != '\n' && !sepmap[ch] ); + } while( ch != EOF && + ch != '\n' && + !sepmap[ch] ); ungetc( ch, fp ); @@ -361,116 +369,6 @@ vips__csv_read_header( const char *filename, VipsImage *out, return( 0 ); } -/* Read the header. Two numbers for width and height, and two optional - * numbers for scale and offset. - */ -static VipsImage * -vips__array_header( char *whitemap, char *sepmap, FILE *fp ) -{ - double header[4]; - int i; - int ch; - VipsImage *out; - - for( i = 0; i < 4 && - (ch = read_double( fp, whitemap, sepmap, - 1, i + 1, &header[i] )) == 0; - i++ ) - ; - - if( i < 2 ) { - vips_error( "mask2vips", "%s", _( "no width / height" ) ); - return( NULL ); - } - if( floor( header[0] ) != header[0] || - floor( header[1] ) != header[1] ) { - vips_error( "mask2vips", "%s", _( "width / height not int" ) ); - return( NULL ); - } - - if( !(out = vips_image_new_array( header[0], header[1] )) ) - return( NULL ); - vips_image_set_double( out, "scale", i > 2 ? header[2] : 1.0 ); - vips_image_set_double( out, "offset", i > 3 ? header[3] : 0.0 ); - - skip_line( fp ); - - return( out ); -} - -static int -vips__array_body( char *whitemap, char *sepmap, VipsImage *out, FILE *fp ) -{ - int x, y; - int ch; - - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize; x++ ) { - double *d = (double *) VIPS_IMAGE_ADDR( out, x, y ); - - ch = read_double( fp, whitemap, sepmap, - y + 1, x + 1, d ); - if( ch == EOF ) { - vips_error( "mask2vips", - _( "unexpected EOF, line %d col %d" ), - y + 1, x + 1 ); - return( -1 ); - } - else if( ch == '\n' ) { - vips_error( "mask2vips", - _( "unexpected EOL, line %d col %d" ), - y + 1, x + 1 ); - return( -1 ); - } - else if( ch ) - /* Parse error. - */ - return( -1 ); - } - - skip_line( fp ); - } - - return( 0 ); -} - -VipsImage * -vips__array_read( const char *filename ) -{ - char whitemap[256]; - char sepmap[256]; - int i; - char *p; - FILE *fp; - VipsImage *out; - - /* Make our char maps. - */ - for( i = 0; i < 256; i++ ) { - whitemap[i] = 0; - sepmap[i] = 0; - } - for( p = " \t\n"; *p; p++ ) - whitemap[(int) *p] = 1; - for( p = ";,"; *p; p++ ) - sepmap[(int) *p] = 1; - - if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) - return( NULL ); - if( !(out = vips__array_header( whitemap, sepmap, fp )) ) { - fclose( fp ); - return( NULL ); - } - if( vips__array_body( whitemap, sepmap, out, fp ) ) { - g_object_unref( out ); - fclose( fp ); - return( NULL ); - } - fclose( fp ); - - return( out ); -} - const char *vips__foreign_csv_suffs[] = { ".csv", NULL }; #define PRINT_INT( TYPE ) fprintf( fp, "%d", *((TYPE*)p) ); @@ -549,4 +447,259 @@ vips__csv_write( VipsImage *in, const char *filename, const char *separator ) return( 0 ); } +/* Read to non-whitespace, or buffer overflow. + */ +static int +fetch_nonwhite( FILE *fp, const char whitemap[256], char *buf, int max ) +{ + int ch; + int i; + + for( i = 0; i < max - 1; i++ ) { + ch = fgetc( fp ); + + if( ch == EOF || ch == '\n' || whitemap[ch] ) + break; + + buf[i] = ch; + } + + buf[i] = '\0'; + + /* We mustn't skip the terminator. + */ + ungetc( ch, fp ); + + return( ch ); +} + +/* Read a single double in ascii (not locale) encoding. + * + * Return the char that caused failure on fail (EOF or \n). + */ +static int +read_ascii_double( FILE *fp, const char whitemap[256], double *out ) +{ + int ch; + char buf[256]; + + ch = skip_white( fp, whitemap ); + + if( ch == EOF || + ch == '\n' ) + return( ch ); + + fetch_nonwhite( fp, whitemap, buf, 256 ); + + *out = g_ascii_strtod( buf, NULL ); + + return( 0 ); +} + +/* Read the header. Two numbers for width and height, and two optional + * numbers for scale and offset. + */ +static int +vips__array_header( char *whitemap, FILE *fp, + int *width, int *height, double *scale, double *offset ) +{ + double header[4]; + double d; + int i; + int ch; + + for( i = 0; i < 4 && + (ch = read_ascii_double( fp, whitemap, &header[i] )) == 0; + i++ ) + ; + + if( i < 2 ) { + vips_error( "mask2vips", "%s", _( "no width / height" ) ); + return( -1 ); + } + if( floor( header[0] ) != header[0] || + floor( header[1] ) != header[1] ) { + vips_error( "mask2vips", "%s", _( "width / height not int" ) ); + return( -1 ); + } + if( i == 3 ) { + vips_error( "mask2vips", "%s", _( "bad scale / offset" ) ); + return( -1 ); + } + if( (ch = read_ascii_double( fp, whitemap, &d )) != '\n' ) { + vips_error( "mask2vips", "%s", _( "extra chars in header" ) ); + return( -1 ); + } + if( i > 2 && + header[2] == 0.0 ) { + vips_error( "mask2vips", "%s", _( "zero scale" ) ); + return( -1 ); + } + + *width = header[0]; + *height = header[0]; + *scale = i > 2 ? header[2] : 1.0; + *offset = i > 2 ? header[3] : 0.0; + + skip_line( fp ); + + return( 0 ); +} + +#define WHITESPACE " \"\t\n;," + +/* Get the header from an array file. + * + * Also read the first line and make sure there are the right number of + * entries. + */ +int +vips__array_read_header( const char *filename, + int *width, int *height, double *scale, double *offset ) +{ + char whitemap[256]; + int i; + char *p; + FILE *fp; + int ch; + double d; + + for( i = 0; i < 256; i++ ) + whitemap[i] = 0; + for( p = WHITESPACE; *p; p++ ) + whitemap[(int) *p] = 1; + + if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) + return( -1 ); + if( vips__array_header( whitemap, fp, + width, height, scale, offset ) ) { + fclose( fp ); + return( -1 ); + } + + for( i = 0; i < *width; i++ ) { + ch = read_ascii_double( fp, whitemap, &d ); + + if( ch ) { + fclose( fp ); + vips_error( "mask2vips", "%s", _( "line too short" ) ); + return( -1 ); + } + } + + /* Deliberately don't check for line too long. + */ + + fclose( fp ); + + return( 0 ); +} + +static int +vips__array_body( char *whitemap, VipsImage *out, FILE *fp ) +{ + int x, y; + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize; x++ ) { + int ch; + double d; + + ch = read_ascii_double( fp, whitemap, &d ); + if( ch == EOF || + ch == '\n' ) { + vips_error( "mask2vips", + _( "line %d too short" ), y + 1 ); + return( -1 ); + } + *((double *) VIPS_IMAGE_ADDR( out, x, y )) = d; + + /* Deliberately don't check for line too long. + */ + } + + skip_line( fp ); + } + + return( 0 ); +} + +VipsImage * +vips__array_read( const char *filename ) +{ + char whitemap[256]; + int i; + char *p; + FILE *fp; + int width; + int height; + double scale; + double offset; + VipsImage *out; + + for( i = 0; i < 256; i++ ) + whitemap[i] = 0; + for( p = WHITESPACE; *p; p++ ) + whitemap[(int) *p] = 1; + + if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) + return( NULL ); + if( vips__array_header( whitemap, fp, + &width, &height, &scale, &offset ) ) { + fclose( fp ); + return( NULL ); + } + + if( !(out = vips_image_new_array( width, height )) ) + return( NULL ); + vips_image_set_double( out, "scale", scale ); + vips_image_set_double( out, "offset", offset ); + + if( vips__array_body( whitemap, out, fp ) ) { + g_object_unref( out ); + fclose( fp ); + return( NULL ); + } + fclose( fp ); + + return( out ); +} + +int +vips__array_write( VipsImage *in, const char *filename ) +{ + VipsImage *mask; + FILE *fp; + int x, y; + + if( vips_check_matrix( "vips2mask", in, &mask ) ) + return( -1 ); + + if( !(fp = vips__file_open_write( filename, TRUE )) ) { + g_object_unref( mask ); + return( -1 ); + } + fprintf( fp, "%d %d ", mask->Xsize, mask->Ysize ); + if( vips_image_get_typeof( mask, "scale" ) && + vips_image_get_typeof( mask, "offset" ) ) + fprintf( fp, "%g %g ", + vips_image_get_scale( mask ), + vips_image_get_offset( mask ) ); + fprintf( fp, "\n" ); + + for( y = 0; y < mask->Ysize; y++ ) { + for( x = 0; x < mask->Xsize; x++ ) + fprintf( fp, "%g ", + *((double *) VIPS_IMAGE_ADDR( mask, x, y )) ); + + fprintf( fp, "\n" ); + } + + g_object_unref( mask ); + fclose( fp ); + + return( 0 ); +} + +const char *vips__foreign_matrix_suffs[] = { ".mat", NULL }; diff --git a/libvips/foreign/csv.h b/libvips/foreign/csv.h index 1c5b55b2..c42aeae9 100644 --- a/libvips/foreign/csv.h +++ b/libvips/foreign/csv.h @@ -45,6 +45,13 @@ int vips__csv_read_header( const char *filename, VipsImage *out, int vips__csv_write( VipsImage *in, const char *filename, const char *separator ); +int vips__array_read_header( const char *filename, + int *width, int *height, double *scale, double *offset ); +VipsImage *vips__array_read( const char *filename ); +int vips__array_write( VipsImage *in, const char *filename ); + +extern const char *vips__foreign_matrix_suffs[]; + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 6298d2d7..589886f4 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1588,6 +1588,8 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_png_buffer_get_type( void ); extern GType vips_foreign_load_csv_get_type( void ); extern GType vips_foreign_save_csv_get_type( void ); + extern GType vips_foreign_load_matrix_get_type( void ); + extern GType vips_foreign_save_matrix_get_type( void ); extern GType vips_foreign_load_fits_get_type( void ); extern GType vips_foreign_save_fits_get_type( void ); extern GType vips_foreign_load_analyze_get_type( void ); @@ -1614,6 +1616,8 @@ vips_foreign_operation_init( void ) vips_foreign_save_ppm_get_type(); vips_foreign_load_csv_get_type(); vips_foreign_save_csv_get_type(); + vips_foreign_load_matrix_get_type(); + vips_foreign_save_matrix_get_type(); vips_foreign_load_analyze_get_type(); vips_foreign_load_raw_get_type(); vips_foreign_save_raw_get_type(); diff --git a/libvips/foreign/matrixload.c b/libvips/foreign/matrixload.c new file mode 100644 index 00000000..6ef36662 --- /dev/null +++ b/libvips/foreign/matrixload.c @@ -0,0 +1,202 @@ +/* load matrix from a file + * + * 5/12/11 + * - from csvload.c + */ + +/* + + 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 + +#include "csv.h" + +typedef struct _VipsForeignLoadMatrix { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadMatrix; + +typedef VipsForeignLoadClass VipsForeignLoadMatrixClass; + +G_DEFINE_TYPE( VipsForeignLoadMatrix, vips_foreign_load_matrix, + VIPS_TYPE_FOREIGN_LOAD ); + +static VipsForeignFlags +vips_foreign_load_matrix_get_flags_filename( const char *filename ) +{ + return( 0 ); +} + +static VipsForeignFlags +vips_foreign_load_matrix_get_flags( VipsForeignLoad *load ) +{ + VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load; + + return( vips_foreign_load_matrix_get_flags_filename( matrix->filename ) ); +} + +static int +vips_foreign_load_matrix_header( VipsForeignLoad *load ) +{ + VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load; + + int width; + int height; + double scale; + double offset; + + if( vips__array_read_header( matrix->filename, + &width, &height, &scale, &offset ) ) + return( -1 ); + + vips_image_init_fields( load->out, + width, height, 1, + VIPS_FORMAT_DOUBLE, + VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); + vips_image_set_double( load->out, "scale", scale ); + vips_image_set_double( load->out, "offset", offset ); + + return( 0 ); +} + +static int +vips_foreign_load_matrix_load( VipsForeignLoad *load ) +{ + VipsForeignLoadMatrix *matrix = (VipsForeignLoadMatrix *) load; + + VipsImage *out; + + if( !(out = vips__array_read( matrix->filename )) ) + return( -1 ); + if( vips_image_write( out, load->real ) ) { + g_object_unref( out ); + return( -1 ); + } + g_object_unref( out ); + + return( 0 ); +} + +static void +vips_foreign_load_matrix_class_init( VipsForeignLoadMatrixClass *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 = "matrixload"; + object_class->description = _( "load matrix from file" ); + + foreign_class->suffs = vips__foreign_matrix_suffs; + + load_class->get_flags_filename = + vips_foreign_load_matrix_get_flags_filename; + load_class->get_flags = vips_foreign_load_matrix_get_flags; + load_class->header = vips_foreign_load_matrix_header; + load_class->load = vips_foreign_load_matrix_load; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadMatrix, filename ), + NULL ); +} + +static void +vips_foreign_load_matrix_init( VipsForeignLoadMatrix *matrix ) +{ +} + +/** + * vips_matrixload: + * @filename: file to load + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Reads a matrix from a file. + * + * Matrix files have a simple format that's supposed to be easy to create with + * a text editor or a spreadsheet. + * + * The first line has four numbers for width, height, scale and + * offset (scale and offset may be omitted, in which case they default to 1.0 + * and 0.0). Scale must be non-zero. Width and height must be positive + * integers. The numbers are separated by any mixture of spaces, commas, + * tabs and quotation marks ("). The scale and offset fields may be + * floating-point, and must use '.' + * as a decimal separator. + * + * Subsequent lines each hold one line of matrix data, with numbers again + * separated by any mixture of spaces, commas, + * tabs and quotation marks ("). The numbers may be floating-point, and must + * use '.' + * as a decimal separator. + * + * Extra characters at the ends of lines or at the end of the file are + * ignored. + * + * See also: vips_csvload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_matrixload( const char *filename, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "matrixload", ap, filename, out ); + va_end( ap ); + + return( result ); +} + diff --git a/libvips/foreign/matrixsave.c b/libvips/foreign/matrixsave.c new file mode 100644 index 00000000..eddd8cbd --- /dev/null +++ b/libvips/foreign/matrixsave.c @@ -0,0 +1,157 @@ +/* save to matrix + * + * 2/7/13 + * - wrap a class around the matrix writer + */ + +/* + + 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_VERBOSE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "csv.h" + +typedef struct _VipsForeignSaveMatrix { + VipsForeignSave parent_object; + + /* Filename for save. + */ + char *filename; + +} VipsForeignSaveMatrix; + +typedef VipsForeignSaveClass VipsForeignSaveMatrixClass; + +G_DEFINE_TYPE( VipsForeignSaveMatrix, vips_foreign_save_matrix, + VIPS_TYPE_FOREIGN_SAVE ); + +static int +vips_foreign_save_matrix_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSaveMatrix *matrix = (VipsForeignSaveMatrix *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_matrix_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__array_write( save->ready, matrix->filename ) ) + return( -1 ); + + return( 0 ); +} + +/* Save a bit of typing. + */ +#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 bandfmt_matrix[10] = { +/* UC C US S UI I F X D DX */ + D, D, D, D, D, D, D, D, D, D +}; + +static void +vips_foreign_save_matrix_class_init( VipsForeignSaveMatrixClass *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 = "matrixsave"; + object_class->description = _( "save image to matrix file" ); + object_class->build = vips_foreign_save_matrix_build; + + foreign_class->suffs = vips__foreign_matrix_suffs; + + save_class->saveable = VIPS_SAVEABLE_MONO; + save_class->format_table = bandfmt_matrix; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveMatrix, filename ), + NULL ); +} + +static void +vips_foreign_save_matrix_init( VipsForeignSaveMatrix *matrix ) +{ +} + +/** + * vips_matrixsave: + * @in: image to save + * @filename: file to write to + * @...: %NULL-terminated list of optional named arguments + * + * Write @in to @filename in matrix format. See vips_matrixload() for a + * description of the format. + * + * See also: vips_matrixload(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_matrixsave( VipsImage *in, const char *filename, ... ) +{ + va_list ap; + int result; + + va_start( ap, filename ); + result = vips_call_split( "matrixsave", ap, in, filename ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index bb94e824..0e60626b 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -402,6 +402,11 @@ int vips_csvload( const char *filename, VipsImage **out, ... ) int vips_csvsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_matrixload( const char *filename, VipsImage **out, ... ) + __attribute__((sentinel)); +int vips_matrixsave( VipsImage *in, const char *filename, ... ) + __attribute__((sentinel)); + int vips_magickload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); diff --git a/libvips/include/vips/header.h b/libvips/include/vips/header.h index 1fc2ff26..898f3a43 100644 --- a/libvips/include/vips/header.h +++ b/libvips/include/vips/header.h @@ -108,6 +108,8 @@ int vips_image_get_xoffset( const VipsImage *image ); int vips_image_get_yoffset( const VipsImage *image ); const char *vips_image_get_filename( const VipsImage *image ); const char *vips_image_get_mode( const VipsImage *image ); +double vips_image_get_scale( const VipsImage *array ); +double vips_image_get_offset( const VipsImage *array ); void *vips_image_get_data( VipsImage *image ); void vips_image_init_fields( VipsImage *image, @@ -122,9 +124,11 @@ int vips_image_copy_fieldsv( VipsImage *out, VipsImage *in1, ... ) int vips_image_copy_fields( VipsImage *out, VipsImage *in ); void vips_image_set( VipsImage *image, const char *field, GValue *value ); -int vips_image_get( VipsImage *image, const char *field, GValue *value_copy ); -int vips_image_get_as_string( VipsImage *image, const char *field, char **out ); -GType vips_image_get_typeof( VipsImage *image, const char *field ); +int vips_image_get( const VipsImage *image, + const char *field, GValue *value_copy ); +int vips_image_get_as_string( const VipsImage *image, + const char *field, char **out ); +GType vips_image_get_typeof( const VipsImage *image, const char *field ); gboolean vips_image_remove( VipsImage *image, const char *field ); typedef void *(*VipsImageMapFn)( VipsImage *image, const char *field, GValue *value, void *a ); @@ -132,17 +136,20 @@ void *vips_image_map( VipsImage *image, VipsImageMapFn fn, void *a ); void vips_image_set_area( VipsImage *image, const char *field, VipsCallbackFn free_fn, void *data ); -int vips_image_get_area( VipsImage *image, const char *field, void **data ); +int vips_image_get_area( const VipsImage *image, + const char *field, void **data ); void vips_image_set_blob( VipsImage *image, const char *field, VipsCallbackFn free_fn, void *data, size_t length ); -int vips_image_get_blob( VipsImage *image, const char *field, +int vips_image_get_blob( const VipsImage *image, const char *field, void **data, size_t *length ); -int vips_image_get_int( VipsImage *image, const char *field, int *out ); +int vips_image_get_int( const VipsImage *image, const char *field, int *out ); void vips_image_set_int( VipsImage *image, const char *field, int i ); -int vips_image_get_double( VipsImage *image, const char *field, double *out ); +int vips_image_get_double( const VipsImage *image, + const char *field, double *out ); void vips_image_set_double( VipsImage *image, const char *field, double d ); -int vips_image_get_string( VipsImage *image, const char *field, char **out ); +int vips_image_get_string( const VipsImage *image, + const char *field, char **out ); void vips_image_set_string( VipsImage *image, const char *field, const char *str ); diff --git a/libvips/iofuncs/error.c b/libvips/iofuncs/error.c index facbc56a..66138146 100644 --- a/libvips/iofuncs/error.c +++ b/libvips/iofuncs/error.c @@ -1173,8 +1173,9 @@ vips_check_hist( const char *domain, VipsImage *im ) * message otherwise. * * @out is set to be @im cast to double and stored in memory. Use - * VIPS_IMAGE_ADDR() to address values in @out. @out is unreffed for you - * when @im is unreffed. + * VIPS_IMAGE_ADDR() to address values in @out. + * + * You must unref @out when you are done with it. * * See also: vips_error(). * @@ -1183,6 +1184,8 @@ vips_check_hist( const char *domain, VipsImage *im ) int vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ) { + *out = NULL; + if( im->Xsize > 1000 || im->Ysize > 1000 ) { vips_error( domain, "%s", _( "matrix image too large" ) ); return( -1 ); @@ -1195,7 +1198,6 @@ vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out ) if( vips_cast( im, out, VIPS_FORMAT_DOUBLE, NULL ) ) return( -1 ); - vips_object_local( im, *out ); if( vips_image_wio_input( *out ) ) return( -1 ); diff --git a/libvips/iofuncs/header.c b/libvips/iofuncs/header.c index dba70c89..13b4c172 100644 --- a/libvips/iofuncs/header.c +++ b/libvips/iofuncs/header.c @@ -546,6 +546,30 @@ vips_image_get_mode( const VipsImage *image ) return( image->mode ); } +double +vips_image_get_scale( const VipsImage *array ) +{ + double scale; + + scale = 1.0; + if( vips_image_get_typeof( array, "scale" ) ) + vips_image_get_double( array, "scale", &scale ); + + return( scale ); +} + +double +vips_image_get_offset( const VipsImage *array ) +{ + double offset; + + offset = 0.0; + if( vips_image_get_typeof( array, "offset" ) ) + vips_image_get_double( array, "offset", &offset ); + + return( offset ); +} + /** * vips_image_get_data: (skip) * @image: image to get data for @@ -857,7 +881,7 @@ vips_image_set( VipsImage *image, const char *field, GValue *value ) * Returns: 0 on success, -1 otherwise. */ int -vips_image_get( VipsImage *image, const char *field, GValue *value_copy ) +vips_image_get( const VipsImage *image, const char *field, GValue *value_copy ) { int i; VipsMeta *meta; @@ -937,7 +961,7 @@ vips_image_get( VipsImage *image, const char *field, GValue *value_copy ) * field of that name. */ GType -vips_image_get_typeof( VipsImage *image, const char *field ) +vips_image_get_typeof( const VipsImage *image, const char *field ) { int i; VipsMeta *meta; @@ -1079,7 +1103,7 @@ vips_image_set_area( VipsImage *image, const char *field, } static int -meta_get_value( VipsImage *image, +meta_get_value( const VipsImage *image, const char *field, GType type, GValue *value_copy ) { if( vips_image_get( image, field, value_copy ) ) @@ -1114,7 +1138,7 @@ meta_get_value( VipsImage *image, * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_area( VipsImage *image, const char *field, void **data ) +vips_image_get_area( const VipsImage *image, const char *field, void **data ) { GValue value_copy = { 0 }; @@ -1171,7 +1195,7 @@ vips_image_set_blob( VipsImage *image, const char *field, * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_blob( VipsImage *image, const char *field, +vips_image_get_blob( const VipsImage *image, const char *field, void **data, size_t *length ) { GValue value_copy = { 0 }; @@ -1199,7 +1223,7 @@ vips_image_get_blob( VipsImage *image, const char *field, * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_int( VipsImage *image, const char *field, int *out ) +vips_image_get_int( const VipsImage *image, const char *field, int *out ) { int i; GValue value_copy = { 0 }; @@ -1265,7 +1289,7 @@ vips_image_set_int( VipsImage *image, const char *field, int i ) * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_double( VipsImage *image, const char *field, double *out ) +vips_image_get_double( const VipsImage *image, const char *field, double *out ) { int i; GValue value_copy = { 0 }; @@ -1333,7 +1357,7 @@ vips_image_set_double( VipsImage *image, const char *field, double d ) * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_string( VipsImage *image, const char *field, char **out ) +vips_image_get_string( const VipsImage *image, const char *field, char **out ) { int i; GValue value_copy = { 0 }; @@ -1396,7 +1420,8 @@ vips_image_set_string( VipsImage *image, const char *field, const char *str ) * Returns: 0 on success, -1 otherwise. */ int -vips_image_get_as_string( VipsImage *image, const char *field, char **out ) +vips_image_get_as_string( const VipsImage *image, + const char *field, char **out ) { GValue value = { 0 }; GType type; diff --git a/libvips/resample/quadratic.c b/libvips/resample/quadratic.c index baf7c6b6..8c7bd814 100644 --- a/libvips/resample/quadratic.c +++ b/libvips/resample/quadratic.c @@ -104,6 +104,16 @@ typedef VipsResampleClass VipsQuadraticClass; G_DEFINE_TYPE( VipsQuadratic, vips_quadratic, VIPS_TYPE_RESAMPLE ); +static void +vips_quadratic_dispose( GObject *gobject ) +{ + VipsQuadratic *quadratic = (VipsQuadratic *) gobject; + + VIPS_UNREF( quadratic->mat ); + + G_OBJECT_CLASS( vips_quadratic_parent_class )->dispose( gobject ); +} + static int vips_quadratic_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop ) @@ -327,6 +337,7 @@ vips_quadratic_class_init( VipsQuadraticClass *class ) VIPS_DEBUG_MSG( "vips_quadratic_class_init\n" ); + gobject_class->dispose = vips_quadratic_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property;