From fe47ce12e9d016f6705f2f5aad5209a9f719b34d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 21 Feb 2020 17:57:28 +0000 Subject: [PATCH] move csvsave to the new target API --- libvips/deprecated/im_csv2vips.c | 15 +- libvips/foreign/csv.c | 346 ------------------------------- libvips/foreign/csvsave.c | 265 +++++++++++++++++++++-- libvips/foreign/foreign.c | 6 +- libvips/foreign/ppmsave.c | 8 +- libvips/include/vips/foreign.h | 2 + 6 files changed, 272 insertions(+), 370 deletions(-) diff --git a/libvips/deprecated/im_csv2vips.c b/libvips/deprecated/im_csv2vips.c index e4e5a860..38d0f928 100644 --- a/libvips/deprecated/im_csv2vips.c +++ b/libvips/deprecated/im_csv2vips.c @@ -61,6 +61,8 @@ im_csv2vips( const char *filename, IMAGE *out ) char mode[FILENAME_MAX]; char *p, *q, *r; + VipsImage *x; + /* Parse mode string. */ im_filename_split( filename, name, mode ); @@ -76,9 +78,18 @@ im_csv2vips( const char *filename, IMAGE *out ) lines = atoi( r ); } - if( vips__csv_read( name, out, - start_skip, lines, whitespace, separator, FALSE ) ) + if( vips_csvload( name, &x, + "skip", start_skip, + "lines", lines, + "whitespace", whitespace, + "separator", separator, + NULL ) ) return( -1 ); + if( vips_image_write( x, out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); return( 0 ); } diff --git a/libvips/foreign/csv.c b/libvips/foreign/csv.c index 70aae08c..3875fb46 100644 --- a/libvips/foreign/csv.c +++ b/libvips/foreign/csv.c @@ -118,352 +118,6 @@ skip_white( FILE *fp, const char whitemap[256] ) return( ch ); } -static int -skip_to_quote( FILE *fp ) -{ - int ch; - - do { - ch = vips__fgetc( fp ); - - /* Ignore \" in strings. - */ - if( ch == '\\' ) - ch = vips__fgetc( fp ); - else if( ch == '"' ) - break; - } while( ch != EOF && - ch != '\n' ); - - ungetc( ch, fp ); - - return( ch ); -} - -static int -skip_to_sep( FILE *fp, const char sepmap[256] ) -{ - int ch; - - do { - ch = vips__fgetc( fp ); - } while( ch != EOF && - ch != '\n' && - !sepmap[ch] ); - - ungetc( ch, fp ); - - return( ch ); -} - -/* Read a single item. Syntax is: - * - * element : - * whitespace* item whitespace* [EOF|EOL|separator] - * - * item : - * double | - * "anything" | - * empty - * - * the anything in quotes can contain " escaped with \ - * - * Return the char that caused failure on fail (EOF or \n). - */ -static int -read_double( FILE *fp, const char whitemap[256], const char sepmap[256], - int lineno, int colno, double *out, gboolean fail ) -{ - int ch; - - /* The fscanf() may change this ... but all other cases need a zero. - */ - *out = 0; - - ch = skip_white( fp, whitemap ); - if( ch == EOF || - ch == '\n' ) - return( ch ); - - if( ch == '"' ) { - (void) vips__fgetc( fp ); - (void) skip_to_quote( fp ); - (void) vips__fgetc( fp ); - } - else if( !sepmap[ch] && - fscanf( fp, "%lf", out ) != 1 ) { - /* Only a warning, since (for example) exported spreadsheets - * will often have text or date fields. - */ - g_warning( _( "error parsing number, line %d, column %d" ), - lineno, colno ); - if( fail ) - return( EOF ); - - /* Step over the bad data to the next separator. - */ - (void) skip_to_sep( fp, sepmap ); - } - - /* Don't need to check result, we have read a field successfully. - */ - ch = skip_white( fp, whitemap ); - - /* If it's a separator, we have to step over it. - */ - if( ch != EOF && - sepmap[ch] ) - (void) vips__fgetc( fp ); - - return( 0 ); -} - -static int -read_csv( FILE *fp, VipsImage *out, - int skip, - int lines, - const char *whitespace, const char *separator, - gboolean read_image, - gboolean fail ) -{ - int i; - char whitemap[256]; - char sepmap[256]; - const char *p; - fpos_t pos; - int columns; - int ch; - double d; - double *buf; - int y; - - /* Make our char maps. - */ - for( i = 0; i < 256; i++ ) { - whitemap[i] = 0; - sepmap[i] = 0; - } - for( p = whitespace; *p; p++ ) - whitemap[(int) *p] = 1; - for( p = separator; *p; p++ ) - sepmap[(int) *p] = 1; - - /* Skip first few lines. - */ - for( i = 0; i < skip; i++ ) - if( !skip_line( fp ) ) { - vips_error( "csv2vips", - "%s", _( "end of file while skipping start" ) ); - return( -1 ); - } - - /* Parse the first line to get number of columns. Only bother checking - * fgetpos() the first time we use it: assume it's working after this. - */ - if( fgetpos( fp, &pos ) ) { - vips_error_system( errno, "csv2vips", - "%s", _( "unable to seek" ) ); - return( -1 ); - } - for( columns = 0; - (ch = read_double( fp, whitemap, sepmap, - skip + 1, columns + 1, &d, fail )) == 0; - columns++ ) - ; - (void) fsetpos( fp, &pos ); - - if( columns == 0 ) { - vips_error( "csv2vips", "%s", _( "empty line" ) ); - return( -1 ); - } - - /* If lines is -1, we have to scan the whole file to get the - * number of lines out. - */ - if( lines == -1 ) { - (void) fgetpos( fp, &pos ); - for( lines = 0; skip_line( fp ); lines++ ) - ; - (void) fsetpos( fp, &pos ); - } - - vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); - vips_image_init_fields( out, - columns, lines, 1, - VIPS_FORMAT_DOUBLE, - VIPS_CODING_NONE, VIPS_INTERPRETATION_B_W, 1.0, 1.0 ); - - /* Just reading the header? We are done. - */ - if( !read_image ) - return( 0 ); - - if( !(buf = VIPS_ARRAY( out, - VIPS_IMAGE_N_ELEMENTS( out ), double )) ) - return( -1 ); - - for( y = 0; y < lines; y++ ) { - int x; - - for( x = 0; x < columns; x++ ) { - int lineno = y + skip + 1; - int colno = x + 1; - - ch = read_double( fp, whitemap, sepmap, - lineno, colno, &d, fail ); - if( ch == EOF ) { - vips_error( "csv2vips", - _( "unexpected EOF, line %d col %d" ), - lineno, colno ); - return( -1 ); - } - else if( ch == '\n' ) { - vips_error( "csv2vips", - _( "unexpected EOL, line %d col %d" ), - lineno, colno ); - return( -1 ); - } - else if( ch ) - /* Parse error. - */ - return( -1 ); - - buf[x] = d; - } - - if( vips_image_write_line( out, y, (VipsPel *) buf ) ) - return( -1 ); - - /* Skip over the '\n' to the next line. - */ - skip_line( fp ); - } - - return( 0 ); -} - -int -vips__csv_read( const char *filename, VipsImage *out, - int skip, int lines, const char *whitespace, const char *separator, - gboolean fail ) -{ - FILE *fp; - - if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) - return( -1 ); - if( read_csv( fp, out, - skip, lines, whitespace, separator, TRUE, fail ) ) { - fclose( fp ); - return( -1 ); - } - fclose( fp ); - - return( 0 ); -} - -int -vips__csv_read_header( const char *filename, VipsImage *out, - int skip, int lines, const char *whitespace, const char *separator, - gboolean fail ) -{ - FILE *fp; - - if( !(fp = vips__file_open_read( filename, NULL, TRUE )) ) - return( -1 ); - if( read_csv( fp, out, - skip, lines, whitespace, separator, FALSE, fail ) ) { - fclose( fp ); - return( -1 ); - } - fclose( fp ); - - return( 0 ); -} - -const char *vips__foreign_csv_suffs[] = { ".csv", NULL }; - -#define PRINT_INT( TYPE ) fprintf( fp, "%d", *((TYPE*)p) ); -#define PRINT_FLOAT( TYPE ) fprintf( fp, "%g", *((TYPE*)p) ); -#define PRINT_COMPLEX( TYPE ) fprintf( fp, "(%g, %g)", \ - ((TYPE*)p)[0], ((TYPE*)p)[1] ); - -static int -vips2csv( VipsImage *in, FILE *fp, const char *sep ) -{ - int w = VIPS_IMAGE_N_ELEMENTS( in ); - int es = VIPS_IMAGE_SIZEOF_ELEMENT( in ); - - int x, y; - VipsPel *p; - - p = in->data; - for( y = 0; y < in->Ysize; y++ ) { - for( x = 0; x < w; x++ ) { - if( x > 0 ) - fprintf( fp, "%s", sep ); - - switch( in->BandFmt ) { - case VIPS_FORMAT_UCHAR: - PRINT_INT( unsigned char ); break; - case VIPS_FORMAT_CHAR: - PRINT_INT( char ); break; - case VIPS_FORMAT_USHORT: - PRINT_INT( unsigned short ); break; - case VIPS_FORMAT_SHORT: - PRINT_INT( short ); break; - case VIPS_FORMAT_UINT: - PRINT_INT( unsigned int ); break; - case VIPS_FORMAT_INT: - PRINT_INT( int ); break; - case VIPS_FORMAT_FLOAT: - PRINT_FLOAT( float ); break; - case VIPS_FORMAT_DOUBLE: - PRINT_FLOAT( double ); break; - case VIPS_FORMAT_COMPLEX: - PRINT_COMPLEX( float ); break; - case VIPS_FORMAT_DPCOMPLEX: - PRINT_COMPLEX( double ); break; - - default: - g_assert_not_reached(); - } - - p += es; - } - - fprintf( fp, "\n" ); - } - - return( 0 ); -} - -int -vips__csv_write( VipsImage *in, const char *filename, const char *separator ) -{ - FILE *fp; - VipsImage *memory; - - if( vips_check_mono( "vips2csv", in ) || - vips_check_uncoded( "vips2csv", in ) || - !(memory = vips_image_copy_memory( in )) ) - return( -1 ); - - if( !(fp = vips__file_open_write( filename, TRUE )) ) { - VIPS_UNREF( memory ); - return( -1 ); - } - - if( vips2csv( memory, fp, separator ) ) { - fclose( fp ); - VIPS_UNREF( memory ); - return( -1 ); - } - fclose( fp ); - VIPS_UNREF( memory ); - - return( 0 ); -} - /* Read to non-whitespace, or buffer overflow. */ static int diff --git a/libvips/foreign/csvsave.c b/libvips/foreign/csvsave.c index 633f9ac9..4bb88fe9 100644 --- a/libvips/foreign/csvsave.c +++ b/libvips/foreign/csvsave.c @@ -2,6 +2,8 @@ * * 2/12/11 * - wrap a class around the csv writer + * 21/2/20 + * - rewrite for the VipsTarget API */ /* @@ -52,9 +54,7 @@ typedef struct _VipsForeignSaveCsv { VipsForeignSave parent_object; - /* Filename for save. - */ - char *filename; + VipsTarget *target; const char *separator; } VipsForeignSaveCsv; @@ -64,17 +64,119 @@ typedef VipsForeignSaveClass VipsForeignSaveCsvClass; G_DEFINE_TYPE( VipsForeignSaveCsv, vips_foreign_save_csv, VIPS_TYPE_FOREIGN_SAVE ); +static void +vips_foreign_save_csv_dispose( GObject *gobject ) +{ + VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) gobject; + + if( csv->target ) + vips_target_finish( csv->target ); + VIPS_UNREF( csv->target ); + + G_OBJECT_CLASS( vips_foreign_save_csv_parent_class )-> + dispose( gobject ); +} + +#define PRINT_INT( TYPE ) { \ + TYPE *pt = (TYPE *) p; \ + \ + for( x = 0; x < image->Xsize; x++ ) { \ + if( x > 0 ) \ + vips_target_writes( csv->target, csv->separator ); \ + vips_target_writef( csv->target, "%d", pt[x] ); \ + } \ +} + +#define PRINT_FLOAT( TYPE ) { \ + TYPE *pt = (TYPE *) p; \ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; \ + \ + for( x = 0; x < image->Xsize; x++ ) { \ + if( x > 0 ) \ + vips_target_writes( csv->target, csv->separator ); \ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, pt[x] ); \ + vips_target_writes( csv->target, buf ); \ + } \ +} + +#define PRINT_COMPLEX( TYPE ) { \ + TYPE *pt = (TYPE *) p; \ + char buf[G_ASCII_DTOSTR_BUF_SIZE]; \ + \ + for( x = 0; x < image->Xsize; x++ ) { \ + if( x > 0 ) \ + vips_target_writes( csv->target, csv->separator ); \ + VIPS_TARGET_PUTC( csv->target, '(' ); \ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, pt[0] ); \ + vips_target_writes( csv->target, buf ); \ + VIPS_TARGET_PUTC( csv->target, ',' ); \ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, pt[1] ); \ + vips_target_writes( csv->target, buf ); \ + VIPS_TARGET_PUTC( csv->target, ')' ); \ + pt += 2; \ + } \ +} + +static int +vips_foreign_save_csv_block( VipsRegion *region, VipsRect *area, void *a ) +{ + VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) a; + VipsImage *image = region->im; + + int x, y; + + for( y = 0; y < area->height; y++ ) { + VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y ); + + switch( image->BandFmt ) { + case VIPS_FORMAT_UCHAR: + PRINT_INT( unsigned char ); break; + case VIPS_FORMAT_CHAR: + PRINT_INT( char ); break; + case VIPS_FORMAT_USHORT: + PRINT_INT( unsigned short ); break; + case VIPS_FORMAT_SHORT: + PRINT_INT( short ); break; + case VIPS_FORMAT_UINT: + PRINT_INT( unsigned int ); break; + case VIPS_FORMAT_INT: + PRINT_INT( int ); break; + case VIPS_FORMAT_FLOAT: + PRINT_FLOAT( float ); break; + case VIPS_FORMAT_DOUBLE: + PRINT_FLOAT( double ); break; + case VIPS_FORMAT_COMPLEX: + PRINT_COMPLEX( float ); break; + case VIPS_FORMAT_DPCOMPLEX: + PRINT_COMPLEX( double ); break; + + default: + g_assert_not_reached(); + } + + if( vips_target_writes( csv->target, "\n" ) ) + return( -1 ); + } + + return( 0 ); +} + static int vips_foreign_save_csv_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( 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 ) ) + if( vips_check_mono( class->nickname, save->ready ) || + vips_check_uncoded( class->nickname, save->ready ) ) + return( -1 ); + + if( vips_sink_disc( save->ready, vips_foreign_save_csv_block, csv ) ) return( -1 ); return( 0 ); @@ -85,27 +187,18 @@ 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->dispose = vips_foreign_save_csv_dispose; 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->nickname = "csvsave_base"; + object_class->description = _( "save image to csv" ); object_class->build = vips_foreign_save_csv_build; - foreign_class->suffs = vips__foreign_csv_suffs; - save_class->saveable = VIPS_SAVEABLE_MONO; - 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" ), @@ -120,6 +213,117 @@ vips_foreign_save_csv_init( VipsForeignSaveCsv *csv ) csv->separator = g_strdup( "\t" ); } +typedef struct _VipsForeignSaveCsvFile { + VipsForeignSaveCsv parent_object; + + char *filename; +} VipsForeignSaveCsvFile; + +typedef VipsForeignSaveCsvClass VipsForeignSaveCsvFileClass; + +G_DEFINE_TYPE( VipsForeignSaveCsvFile, vips_foreign_save_csv_file, + vips_foreign_save_csv_get_type() ); + +static int +vips_foreign_save_csv_file_build( VipsObject *object ) +{ + VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; + VipsForeignSaveCsvFile *file = (VipsForeignSaveCsvFile *) object; + + if( file->filename && + !(csv->target = vips_target_new_to_file( file->filename )) ) + return( -1 ); + + return( VIPS_OBJECT_CLASS( vips_foreign_save_csv_file_parent_class )-> + build( object ) ); +} + +static const char *vips_foreign_save_csv_file_suffs[] = { + ".csv", + NULL +}; + +static void +vips_foreign_save_csv_file_class_init( VipsForeignSaveCsvFileClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "csvsave"; + object_class->build = vips_foreign_save_csv_file_build; + + foreign_class->suffs = vips_foreign_save_csv_file_suffs; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveCsvFile, filename ), + NULL ); + +} + +static void +vips_foreign_save_csv_file_init( VipsForeignSaveCsvFile *file ) +{ +} + +typedef struct _VipsForeignSaveCsvTarget { + VipsForeignSaveCsv parent_object; + + VipsTarget *target; +} VipsForeignSaveCsvTarget; + +typedef VipsForeignSaveCsvClass VipsForeignSaveCsvTargetClass; + +G_DEFINE_TYPE( VipsForeignSaveCsvTarget, vips_foreign_save_csv_target, + vips_foreign_save_csv_get_type() ); + +static int +vips_foreign_save_csv_target_build( VipsObject *object ) +{ + VipsForeignSaveCsv *csv = (VipsForeignSaveCsv *) object; + VipsForeignSaveCsvTarget *target = (VipsForeignSaveCsvTarget *) object; + + if( target->target ) { + csv->target = target->target; + g_object_ref( csv->target ); + } + + return( VIPS_OBJECT_CLASS( vips_foreign_save_csv_target_parent_class )-> + build( object ) ); +} + +static void +vips_foreign_save_csv_target_class_init( VipsForeignSaveCsvTargetClass *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 = "csvsave_target"; + object_class->build = vips_foreign_save_csv_target_build; + + VIPS_ARG_OBJECT( class, "target", 1, + _( "Target" ), + _( "Target to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveCsvTarget, target ), + VIPS_TYPE_TARGET ); + +} + +static void +vips_foreign_save_csv_target_init( VipsForeignSaveCsvTarget *target ) +{ +} + /** * vips_csvsave: (method) * @in: image to save @@ -155,3 +359,32 @@ vips_csvsave( VipsImage *in, const char *filename, ... ) return( result ); } + +/** + * vips_csvsave_target: (method) + * @in: image to save + * @target: save image to this target + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * * @separator: separator string + * + * As vips_csvsave(), but save to a target. + * + * See also: vips_csvsave(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_csvsave_target( VipsImage *in, VipsTarget *target, ... ) +{ + va_list ap; + int result; + + va_start( ap, target ); + result = vips_call_split( "csvsave_target", ap, in, target ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 5eee66d8..56826faf 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -2036,7 +2036,8 @@ vips_foreign_operation_init( 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_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_get_type( void ); extern GType vips_foreign_save_matrix_get_type( void ); @@ -2112,7 +2113,8 @@ vips_foreign_operation_init( void ) vips_foreign_load_csv_file_get_type(); vips_foreign_load_csv_source_get_type(); - vips_foreign_save_csv_get_type(); + vips_foreign_save_csv_file_get_type(); + vips_foreign_save_csv_target_get_type(); vips_foreign_load_matrix_get_type(); vips_foreign_save_matrix_get_type(); vips_foreign_print_matrix_get_type(); diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c index 25f59a29..0f733777 100644 --- a/libvips/foreign/ppmsave.c +++ b/libvips/foreign/ppmsave.c @@ -186,10 +186,11 @@ vips_foreign_save_ppm_block( VipsRegion *region, VipsRect *area, void *a ) { VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) a; VipsImage *image = region->im; - int i; - for( i = 0; i < area->height; i++ ) { - VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i ); + int y; + + for( y = 0; y < area->height; y++ ) { + VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y ); if( ppm->fn( ppm, image, p ) ) return( -1 ); @@ -450,7 +451,6 @@ vips_foreign_save_ppm_file_init( VipsForeignSavePpmFile *file ) { } - #endif /*HAVE_PPM*/ /** diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index ed04cfd0..6989da10 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -513,6 +513,8 @@ int vips_csvload_source( VipsSource *source, VipsImage **out, ... ) __attribute__((sentinel)); int vips_csvsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_csvsave_target( VipsImage *in, VipsTarget *target, ... ) + __attribute__((sentinel)); int vips_matrixload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel));