diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index b44cf53c..30f392c4 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -16,7 +16,6 @@ libforeign_la_SOURCES = \ radiance.c \ radload.c \ radsave.c \ - ppm.c \ ppmload.c \ ppmsave.c \ csv.c \ diff --git a/libvips/foreign/dzsave.c b/libvips/foreign/dzsave.c index 6d6ec536..3f3000f3 100644 --- a/libvips/foreign/dzsave.c +++ b/libvips/foreign/dzsave.c @@ -1135,22 +1135,7 @@ build_scan_properties( VipsImage *image ) char *date; int i; -#ifdef HAVE_DATE_TIME_FORMAT_ISO8601 -{ - GDateTime *now; - - now = g_date_time_new_now_local(); - date = g_date_time_format_iso8601( now ); - g_date_time_unref( now ); -} -#else /*!HAVE_DATE_TIME_FORMAT_ISO8601*/ -{ - GTimeVal now; - - g_get_current_time( &now ); - date = g_time_val_to_iso8601( &now ); -} -#endif /*HAVE_DATE_TIME_FORMAT_ISO8601*/ + date = vips__get_iso8601(); vips_dbuf_init( &dbuf ); vips_dbuf_writef( &dbuf, "\n" ); diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 9d8ca8c5..129dd1b9 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1993,7 +1993,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_rad_stream_get_type( void ); extern GType vips_foreign_load_mat_get_type( void ); extern GType vips_foreign_load_ppm_file_get_type( void ); - extern GType vips_foreign_save_ppm_get_type( void ); + extern GType vips_foreign_save_ppm_file_get_type( void ); extern GType vips_foreign_load_png_get_type( void ); extern GType vips_foreign_load_png_buffer_get_type( void ); extern GType vips_foreign_load_png_stream_get_type( void ); @@ -2077,7 +2077,7 @@ vips_foreign_operation_init( void ) #ifdef HAVE_PPM vips_foreign_load_ppm_file_get_type(); - vips_foreign_save_ppm_get_type(); + vips_foreign_save_ppm_file_get_type(); #endif /*HAVE_PPM*/ #ifdef HAVE_RADIANCE diff --git a/libvips/foreign/ppm.c b/libvips/foreign/ppm.c deleted file mode 100644 index d8450d86..00000000 --- a/libvips/foreign/ppm.c +++ /dev/null @@ -1,330 +0,0 @@ -/* Read a ppm file. - * - - * 4/2/10 - * - gtkdoc - - * 13/11/19 - * - redone with streams - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#ifdef HAVE_PPM - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "pforeign.h" - -struct _Write; - -typedef int (*write_fn)( struct _Write *write, VipsPel *p ); - -/* What we track during a PPM write. - */ -typedef struct _Write { - VipsImage *in; - VipsStreamo *streamo; - write_fn fn; -} Write; - -static void -write_destroy( Write *write ) -{ - if( write->streamo ) - vips_streamo_finish( write->streamo ); - VIPS_UNREF( write->streamo ); - - vips_free( write ); -} - -static Write * -write_new( VipsImage *in, VipsStreamo *streamo ) -{ - Write *write; - - if( !(write = VIPS_NEW( NULL, Write )) ) - return( NULL ); - - write->in = in; - write->streamo = streamo; - g_object_ref( streamo ); - write->fn = NULL; - - return( write ); -} - -static int -write_ppm_line_ascii( Write *write, VipsPel *p ) -{ - const int sk = VIPS_IMAGE_SIZEOF_PEL( write->in ); - int x, k; - - for( x = 0; x < write->in->Xsize; x++ ) { - for( k = 0; k < write->in->Bands; k++ ) { - switch( write->in->BandFmt ) { - case VIPS_FORMAT_UCHAR: - vips_streamo_writef( write->streamo, - "%d ", p[k] ); - break; - - case VIPS_FORMAT_USHORT: - vips_streamo_writef( write->streamo, - "%d ", ((unsigned short *) p)[k] ); - break; - - case VIPS_FORMAT_UINT: - vips_streamo_writef( write->streamo, - "%d ", ((unsigned int *) p)[k] ); - break; - - default: - g_assert_not_reached(); - } - } - - p += sk; - } - - if( vips_streamo_writef( write->streamo, "\n" ) ) - return( -1 ); - - return( 0 ); -} - -static int -write_ppm_line_ascii_squash( Write *write, VipsPel *p ) -{ - int x; - - for( x = 0; x < write->in->Xsize; x++ ) - vips_streamo_writef( write->streamo, "%d ", p[x] ? 0 : 1 ); - - if( vips_streamo_writef( write->streamo, "\n" ) ) - return( -1 ); - - return( 0 ); -} - -static int -write_ppm_line_binary( Write *write, VipsPel *p ) -{ - if( vips_streamo_write( write->streamo, - p, VIPS_IMAGE_SIZEOF_LINE( write->in ) ) ) - return( -1 ); - - return( 0 ); -} - -static int -write_ppm_line_binary_squash( Write *write, VipsPel *p ) -{ - int x; - int bits; - int n_bits; - - bits = 0; - n_bits = 0; - for( x = 0; x < write->in->Xsize; x++ ) { - bits = VIPS_LSHIFT_INT( bits, 1 ); - n_bits += 1; - bits |= p[x] ? 0 : 1; - - if( n_bits == 8 ) { - if( VIPS_STREAMO_PUTC( write->streamo, bits ) ) - return( -1 ); - - bits = 0; - n_bits = 0; - } - } - - /* Flush any remaining bits in this line. - */ - if( n_bits && - VIPS_STREAMO_PUTC( write->streamo, bits ) ) - return( -1 ); - - return( 0 ); -} - -static int -write_ppm_block( VipsRegion *region, VipsRect *area, void *a ) -{ - Write *write = (Write *) a; - int i; - - for( i = 0; i < area->height; i++ ) { - VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i ); - - if( write->fn( write, p ) ) - return( -1 ); - } - - return( 0 ); -} - -static int -write_ppm( Write *write, gboolean ascii, gboolean squash ) -{ - VipsImage *in = write->in; - - char *magic; - time_t timebuf; - - magic = "unset"; - if( in->BandFmt == VIPS_FORMAT_FLOAT && in->Bands == 3 ) - magic = "PF"; - else if( in->BandFmt == VIPS_FORMAT_FLOAT && in->Bands == 1 ) - magic = "Pf"; - else if( in->Bands == 1 && ascii && squash ) - magic = "P1"; - else if( in->Bands == 1 && ascii ) - magic = "P2"; - else if( in->Bands == 1 && !ascii && squash ) - magic = "P4"; - else if( in->Bands == 1 && !ascii ) - magic = "P5"; - else if( in->Bands == 3 && ascii ) - magic = "P3"; - else if( in->Bands == 3 && !ascii ) - magic = "P6"; - else - g_assert_not_reached(); - - vips_streamo_writef( write->streamo, "%s\n", magic ); - time( &timebuf ); - vips_streamo_writef( write->streamo, - "#vips2ppm - %s\n", ctime( &timebuf ) ); - vips_streamo_writef( write->streamo, "%d %d\n", in->Xsize, in->Ysize ); - - if( !squash ) - switch( in->BandFmt ) { - case VIPS_FORMAT_UCHAR: - vips_streamo_writef( write->streamo, - "%d\n", UCHAR_MAX ); - break; - - case VIPS_FORMAT_USHORT: - vips_streamo_writef( write->streamo, - "%d\n", USHRT_MAX ); - break; - - case VIPS_FORMAT_UINT: - vips_streamo_writef( write->streamo, - "%d\n", UINT_MAX ); - break; - - case VIPS_FORMAT_FLOAT: -{ - double scale; - - if( vips_image_get_double( in, "pfm-scale", &scale ) ) - scale = 1; - if( !vips_amiMSBfirst() ) - scale *= -1; - vips_streamo_writef( write->streamo, - "%g\n", scale ); -} - break; - - default: - g_assert_not_reached(); - } - - if( squash ) - write->fn = ascii ? - write_ppm_line_ascii_squash : - write_ppm_line_binary_squash; - else - write->fn = ascii ? - write_ppm_line_ascii : - write_ppm_line_binary; - - if( vips_sink_disc( write->in, write_ppm_block, write ) ) - return( -1 ); - - return( 0 ); -} - -int -vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo, - gboolean ascii, gboolean squash ) -{ - Write *write; - - if( vips_check_uintorf( "vips2ppm", in ) || - vips_check_bands_1or3( "vips2ppm", in ) || - vips_check_uncoded( "vips2ppm", in ) || - vips_image_pio_input( in ) ) - return( -1 ); - - if( ascii && - in->BandFmt == VIPS_FORMAT_FLOAT ) { - g_warning( "%s", - _( "float images must be binary -- disabling ascii" ) ); - ascii = FALSE; - } - - /* One bit images must come from a 8 bit, one band source. - */ - if( squash && - (in->Bands != 1 || - in->BandFmt != VIPS_FORMAT_UCHAR) ) { - g_warning( "%s", - _( "can only squash 1 band uchar images -- " - "disabling squash" ) ); - squash = FALSE; - } - - if( !(write = write_new( in, streamo )) ) - return( -1 ); - - if( write_ppm( write, ascii, squash ) ) { - write_destroy( write ); - return( -1 ); - } - write_destroy( write ); - - return( 0 ); -} - -#endif /*HAVE_PPM*/ diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c index c2d08d34..50365785 100644 --- a/libvips/foreign/ppmload.c +++ b/libvips/foreign/ppmload.c @@ -62,6 +62,12 @@ */ +/* TODO + * + * - make partial + * - only use mmap on filename streams? + */ + /* #define DEBUG */ @@ -147,110 +153,32 @@ vips_foreign_load_ppm_is_a_stream( VipsStreami *streami ) return( FALSE ); } -/* The largest number/field/whatever we can read. - */ -#define MAX_THING (80) - -/* After this, the next getc will be the first char of the next line (or EOF). - */ -static int -skip_line( VipsStreamib *streamib ) -{ - int ch; - - while( (ch = VIPS_STREAMIB_GETC( streamib )) != '\n' && - ch != EOF ) - ; - - if( ch == EOF ) - return( -1 ); - - return( 0 ); -} - -/* After this, the next getc will be the first char of the next block of - * non-whitespace (or EOF). - */ -static int -skip_white_space( VipsStreamib *streamib ) -{ - int ch; - - while( isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) ) - ; - VIPS_STREAMIB_UNGETC( streamib ); - - /* # skip comments too. - */ - if( ch == '#' && - (skip_line( streamib ) || - skip_white_space( streamib )) ) - return( -1 ); - - return( 0 ); -} - -/* After this, the next getc will be the first char of the next block of - * whitespace (or EOF). buf will be filled with up to length bytes, and - * null-terminated. - * - * If the first getc is whitespace, stop instantly and return nothing. - */ static int -read_non_white_space( VipsStreamib *streamib, char *buf, int length ) +get_int( VipsStreamib *streamib, int *i ) { - int ch; - int i; + const char *txt; - for( i = 0; i < length - 1 && - !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && - ch != EOF; i++ ) - buf[i] = ch; - buf[i] = '\0'; + if( vips_streamib_skip_whitespace( streamib ) || + !(txt = vips_streamib_get_non_whitespace( streamib )) ) + return( -1 ); - /* If we stopped before seeing any whitespace, skip to the end of the - * block of non-whitespace. - */ - if( !isspace( ch ) ) - while( !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && - ch != EOF ) - ; - - /* If we finally stopped on whitespace, step back one so the next get - * will be whitespace (or EOF). - */ - if( isspace( ch ) ) - VIPS_STREAMIB_UNGETC( streamib ); + *i = atoi( txt ); return( 0 ); } static int -read_int( VipsStreamib *streamib, int *i ) +get_float( VipsStreamib *streamib, float *f ) { - char buf[MAX_THING]; + const char *txt; - if( skip_white_space( streamib ) ) + if( vips_streamib_skip_whitespace( streamib ) || + !(txt = vips_streamib_get_non_whitespace( streamib )) ) return( -1 ); - read_non_white_space( streamib, buf, MAX_THING ); - *i = atoi( buf ); - - return( 0 ); -} - -static int -read_float( VipsStreamib *streamib, float *f ) -{ - char buf[MAX_THING]; - - if( skip_white_space( streamib ) ) - return( -1 ); - - read_non_white_space( streamib, buf, MAX_THING ); /* We don't want the locale str -> float conversion. */ - *f = g_ascii_strtod( buf, NULL ); + *f = g_ascii_strtod( txt, NULL ); return( 0 ); } @@ -289,6 +217,9 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) 1, 1, 1, 0, 0, 0, 0, 0 }; + if( vips_streami_rewind( ppm->streami ) ) + return( -1 ); + /* Read in the magic number. */ buf[0] = VIPS_STREAMIB_GETC( ppm->streamib ); @@ -312,8 +243,8 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) /* Read in size. */ - if( read_int( ppm->streamib, &ppm->width ) || - read_int( ppm->streamib, &ppm->height ) ) + if( get_int( ppm->streamib, &ppm->width ) || + get_int( ppm->streamib, &ppm->height ) ) return( -1 ); /* Read in max value / scale for >1 bit images. @@ -321,7 +252,7 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) if( ppm->bits > 1 ) { if( ppm->index == 6 || ppm->index == 7 ) { - if( read_float( ppm->streamib, &ppm->scale ) ) + if( get_float( ppm->streamib, &ppm->scale ) ) return( -1 ); /* Scale > 0 means big-endian. @@ -329,7 +260,7 @@ vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) ppm->msb_first = ppm->scale > 0; } else { - if( read_int( ppm->streamib, &ppm->max_value ) ) + if( get_int( ppm->streamib, &ppm->max_value ) ) return( -1 ); if( ppm->max_value > 255 ) @@ -445,6 +376,8 @@ vips_foreign_load_ppm_header( VipsForeignLoad *load ) vips_foreign_load_ppm_set_image( ppm, load->out ); + vips_streami_minimise( ppm->streami ); + return( 0 ); } @@ -499,7 +432,7 @@ load_1bit_ascii( VipsForeignLoadPpm *ppm, VipsImage *image ) for( x = 0; x < image->Xsize; x++ ) { int val; - if( read_int( ppm->streamib, &val ) ) + if( get_int( ppm->streamib, &val ) ) return( -1 ); if( val ) @@ -567,7 +500,7 @@ load_ascii( VipsForeignLoadPpm *ppm, VipsImage *image ) for( x = 0; x < image->Xsize * image->Bands; x++ ) { int val; - if( read_int( ppm->streamib, &val ) ) + if( get_int( ppm->streamib, &val ) ) return( -1 ); switch( image->BandFmt ) { @@ -620,9 +553,12 @@ vips_foreign_load_ppm_load( VipsForeignLoad *load ) else loader = load_ascii; - if( loader( ppm, load->real ) ) + if( vips_streami_decode( ppm->streami ) || + loader( ppm, load->real ) ) return( -1 ); + vips_streami_minimise( ppm->streami ); + return( 0 ); } diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c index 3b986d0f..0ea64f0b 100644 --- a/libvips/foreign/ppmsave.c +++ b/libvips/foreign/ppmsave.c @@ -46,44 +46,292 @@ #include #include +#include #include "pforeign.h" #ifdef HAVE_PPM +typedef struct _VipsForeignSavePpm VipsForeignSavePpm; + +typedef int (*VipsSavePpmFn)( VipsForeignSavePpm *, VipsImage *, VipsPel * ); + typedef struct _VipsForeignSavePpm { VipsForeignSave parent_object; - char *filename; + VipsStreamo *streamo; gboolean ascii; gboolean squash; + + VipsSavePpmFn fn; } VipsForeignSavePpm; typedef VipsForeignSaveClass VipsForeignSavePpmClass; -G_DEFINE_TYPE( VipsForeignSavePpm, vips_foreign_save_ppm, +G_DEFINE_ABSTRACT_TYPE( VipsForeignSavePpm, vips_foreign_save_ppm, VIPS_TYPE_FOREIGN_SAVE ); +static void +vips_foreign_save_ppm_dispose( GObject *gobject ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) gobject; + + if( ppm->streamo ) + vips_streamo_finish( ppm->streamo ); + VIPS_UNREF( ppm->streamo ); + + G_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> + dispose( gobject ); +} + +static int +vips_foreign_save_ppm_line_ascii( VipsForeignSavePpm *ppm, + VipsImage *image, VipsPel *p ) +{ + const int n_elements = image->Xsize * image->Bands; + + int i; + + for( i = 0; i < n_elements; i++ ) { + switch( image->BandFmt ) { + case VIPS_FORMAT_UCHAR: + vips_streamo_writef( ppm->streamo, + "%d ", p[i] ); + break; + + case VIPS_FORMAT_USHORT: + vips_streamo_writef( ppm->streamo, + "%d ", ((unsigned short *) p)[i] ); + break; + + case VIPS_FORMAT_UINT: + vips_streamo_writef( ppm->streamo, + "%d ", ((unsigned int *) p)[i] ); + break; + + default: + g_assert_not_reached(); + } + } + + if( vips_streamo_writes( ppm->streamo, "\n" ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_save_ppm_line_ascii_squash( VipsForeignSavePpm *ppm, + VipsImage *image, VipsPel *p ) +{ + int x; + + for( x = 0; x < image->Xsize; x++ ) + vips_streamo_writef( ppm->streamo, "%d ", p[x] ? 0 : 1 ); + + if( vips_streamo_writes( ppm->streamo, "\n" ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_save_ppm_line_binary( VipsForeignSavePpm *ppm, + VipsImage *image, VipsPel *p ) +{ + if( vips_streamo_write( ppm->streamo, + p, VIPS_IMAGE_SIZEOF_LINE( image ) ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_save_ppm_line_binary_squash( VipsForeignSavePpm *ppm, + VipsImage *image, VipsPel *p ) +{ + int x; + int bits; + int n_bits; + + bits = 0; + n_bits = 0; + for( x = 0; x < image->Xsize; x++ ) { + bits = VIPS_LSHIFT_INT( bits, 1 ); + n_bits += 1; + bits |= p[x] ? 0 : 1; + + if( n_bits == 8 ) { + if( VIPS_STREAMO_PUTC( ppm->streamo, bits ) ) + return( -1 ); + + bits = 0; + n_bits = 0; + } + } + + /* Flush any remaining bits in this line. + */ + if( n_bits && + VIPS_STREAMO_PUTC( ppm->streamo, bits ) ) + return( -1 ); + + return( 0 ); +} + +static int +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 ); + + if( ppm->fn( ppm, image, p ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +vips_foreign_save_ppm( VipsForeignSavePpm *ppm, VipsImage *image ) +{ + char *magic; + char *date; + + magic = "unset"; + if( image->BandFmt == VIPS_FORMAT_FLOAT && + image->Bands == 3 ) + magic = "PF"; + else if( image->BandFmt == VIPS_FORMAT_FLOAT && + image->Bands == 1 ) + magic = "Pf"; + else if( image->Bands == 1 && + ppm->ascii && + ppm->squash ) + magic = "P1"; + else if( image->Bands == 1 && + ppm->ascii ) + magic = "P2"; + else if( image->Bands == 1 && + !ppm->ascii && + ppm->squash ) + magic = "P4"; + else if( image->Bands == 1 && + !ppm->ascii ) + magic = "P5"; + else if( image->Bands == 3 && + ppm->ascii ) + magic = "P3"; + else if( image->Bands == 3 && + !ppm->ascii ) + magic = "P6"; + else + g_assert_not_reached(); + + vips_streamo_writef( ppm->streamo, "%s\n", magic ); + date = vips__get_iso8601(); + vips_streamo_writef( ppm->streamo, + "#vips2ppm - %s\n", date ); + g_free( date ); + vips_streamo_writef( ppm->streamo, + "%d %d\n", image->Xsize, image->Ysize ); + + if( !ppm->squash ) + switch( image->BandFmt ) { + case VIPS_FORMAT_UCHAR: + vips_streamo_writef( ppm->streamo, + "%d\n", UCHAR_MAX ); + break; + + case VIPS_FORMAT_USHORT: + vips_streamo_writef( ppm->streamo, + "%d\n", USHRT_MAX ); + break; + + case VIPS_FORMAT_UINT: + vips_streamo_writef( ppm->streamo, + "%d\n", UINT_MAX ); + break; + + case VIPS_FORMAT_FLOAT: +{ + double scale; + char buf[G_ASCII_DTOSTR_BUF_SIZE]; + + if( vips_image_get_double( image, + "pfm-scale", &scale ) ) + scale = 1; + if( !vips_amiMSBfirst() ) + scale *= -1; + /* Need to be locale independent. + */ + g_ascii_dtostr( buf, G_ASCII_DTOSTR_BUF_SIZE, scale ); + vips_streamo_writes( ppm->streamo, buf ); +} + break; + + default: + g_assert_not_reached(); + } + + if( ppm->squash ) + ppm->fn = ppm->ascii ? + vips_foreign_save_ppm_line_ascii_squash : + vips_foreign_save_ppm_line_binary_squash; + else + ppm->fn = ppm->ascii ? + vips_foreign_save_ppm_line_ascii : + vips_foreign_save_ppm_line_binary; + + if( vips_sink_disc( image, vips_foreign_save_ppm_block, ppm ) ) + return( -1 ); + + return( 0 ); +} + static int vips_foreign_save_ppm_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; - VipsStreamo *streamo; + VipsImage *image; if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> build( object ) ) return( -1 ); - if( !(streamo = vips_streamo_new_to_filename( ppm->filename )) ) - return( -1 ); - if( vips__ppm_save_stream( save->ready, streamo, - ppm->ascii, ppm->squash ) ) { - VIPS_UNREF( streamo ); + image = save->ready; + if( vips_check_uintorf( "vips2ppm", image ) || + vips_check_bands_1or3( "vips2ppm", image ) || + vips_check_uncoded( "vips2ppm", image ) || + vips_image_pio_input( image ) ) return( -1 ); + + if( ppm->ascii && + image->BandFmt == VIPS_FORMAT_FLOAT ) { + g_warning( "%s", + _( "float images must be binary -- disabling ascii" ) ); + ppm->ascii = FALSE; } - VIPS_UNREF( streamo ); + + /* One bit images must come from a 8 bit, one band source. + */ + if( ppm->squash && + (image->Bands != 1 || + image->BandFmt != VIPS_FORMAT_UCHAR) ) { + g_warning( "%s", + _( "can only squash 1 band uchar images -- " + "disabling squash" ) ); + ppm->squash = FALSE; + } + + if( vips_foreign_save_ppm( ppm, image ) ) + return( -1 ); return( 0 ); } @@ -114,11 +362,12 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; + gobject_class->dispose = vips_foreign_save_ppm_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "ppmsave"; - object_class->description = _( "save image to ppm file" ); + object_class->nickname = "ppmsave_base"; + object_class->description = _( "save to ppm" ); object_class->build = vips_foreign_save_ppm_build; foreign_class->suffs = vips__ppm_suffs; @@ -126,13 +375,6 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) save_class->saveable = VIPS_SAVEABLE_RGB; save_class->format_table = bandfmt_ppm; - VIPS_ARG_STRING( class, "filename", 1, - _( "Filename" ), - _( "Filename to save to" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignSavePpm, filename ), - NULL ); - VIPS_ARG_BOOL( class, "ascii", 10, _( "ASCII" ), _( "save as ascii" ), @@ -146,6 +388,7 @@ vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *class ) VIPS_ARGUMENT_OPTIONAL_INPUT, G_STRUCT_OFFSET( VipsForeignSavePpm, squash ), FALSE ); + } static void @@ -153,6 +396,60 @@ vips_foreign_save_ppm_init( VipsForeignSavePpm *ppm ) { } +typedef struct _VipsForeignSavePpmFile { + VipsForeignSavePpm parent_object; + + char *filename; +} VipsForeignSavePpmFile; + +typedef VipsForeignSavePpmClass VipsForeignSavePpmFileClass; + +G_DEFINE_TYPE( VipsForeignSavePpmFile, vips_foreign_save_ppm_file, + vips_foreign_save_ppm_get_type() ); + +static int +vips_foreign_save_ppm_file_build( VipsObject *object ) +{ + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; + VipsForeignSavePpmFile *file = (VipsForeignSavePpmFile *) object; + + if( file->filename && + !(ppm->streamo = + vips_streamo_new_to_filename( file->filename )) ) + return( -1 ); + + return( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_file_parent_class )-> + build( object ) ); +} + +static void +vips_foreign_save_ppm_file_class_init( VipsForeignSavePpmFileClass *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 = "ppmsave"; + object_class->description = _( "save image to ppm file" ); + object_class->build = vips_foreign_save_ppm_file_build; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePpmFile, filename ), + NULL ); + +} + +static void +vips_foreign_save_ppm_file_init( VipsForeignSavePpmFile *file ) +{ +} + + #endif /*HAVE_PPM*/ /** diff --git a/libvips/foreign/radiance.c b/libvips/foreign/radiance.c index c6bcca02..360045d5 100644 --- a/libvips/foreign/radiance.c +++ b/libvips/foreign/radiance.c @@ -506,11 +506,11 @@ getheader( /* get header from file */ ) { for(;;) { - const unsigned char *line; + const char *line; if( !(line = vips_streamib_get_line( streamib )) ) return( -1 ); - if( strcmp( (char *) line, "" ) == 0 ) + if( strcmp( line, "" ) == 0 ) /* Blank line. We've parsed the header successfully. */ break; @@ -725,14 +725,14 @@ int vips__rad_israd( VipsStreami *streami ) { VipsStreamib *streamib; - const unsigned char *line; + const char *line; int result; /* Just test that the first line is the magic string. */ streamib = vips_streamib_new( streami ); result = (line = vips_streamib_get_line( streamib )) && - strcmp( (char *) line, "#?RADIANCE" ) == 0; + strcmp( line, "#?RADIANCE" ) == 0; VIPS_UNREF( streamib ); return( result ); @@ -832,7 +832,7 @@ static int rad2vips_get_header( Read *read, VipsImage *out ) { VipsInterpretation interpretation; - const unsigned char *line; + const char *line; int width; int height; int i, j; @@ -1082,7 +1082,7 @@ vips2rad_put_header( Write *write ) { vips2rad_make_header( write ); - vips_streamo_writef( write->streamo, "#?RADIANCE\n" ); + vips_streamo_writes( write->streamo, "#?RADIANCE\n" ); vips_streamo_writef( write->streamo, "%s%s\n", FMTSTR, write->format ); vips_streamo_writef( write->streamo, "%s%e\n", EXPOSSTR, write->expos ); vips_streamo_writef( write->streamo, @@ -1099,9 +1099,9 @@ vips2rad_put_header( Write *write ) write->prims[GRN][CIEX], write->prims[GRN][CIEY], write->prims[BLU][CIEX], write->prims[BLU][CIEY], write->prims[WHT][CIEX], write->prims[WHT][CIEY] ); - vips_streamo_writef( write->streamo, "\n" ); - vips_streamo_writef( write->streamo, - "%s", resolu2str( resolu_buf, &write->rs ) ); + vips_streamo_writes( write->streamo, "\n" ); + vips_streamo_writes( write->streamo, + resolu2str( resolu_buf, &write->rs ) ); return( 0 ); } diff --git a/libvips/include/vips/stream.h b/libvips/include/vips/stream.h index 34ed9b5b..533f13c6 100644 --- a/libvips/include/vips/stream.h +++ b/libvips/include/vips/stream.h @@ -297,8 +297,10 @@ int vips_streamib_require( VipsStreamib *streamib, int require ); #define VIPS_STREAMIB_PEEK( S ) ((S)->input_buffer + (S)->read_point) #define VIPS_STREAMIB_FETCH( S ) ((S)->input_buffer[(S)->read_point++]) -const unsigned char *vips_streamib_get_line( VipsStreamib *streamib ); -unsigned char *vips_streamib_get_line_copy( VipsStreamib *streamib ); +const char *vips_streamib_get_line( VipsStreamib *streamib ); +char *vips_streamib_get_line_copy( VipsStreamib *streamib ); +const char *vips_streamib_get_non_whitespace( VipsStreamib *streamib ); +int vips_streamib_skip_whitespace( VipsStreamib *streamib ); #define VIPS_TYPE_STREAMIW (vips_streamiw_get_type()) #define VIPS_STREAMIW( obj ) \ @@ -400,13 +402,12 @@ int vips_streamo_write( VipsStreamo *streamo, const void *data, size_t length ); void vips_streamo_finish( VipsStreamo *streamo ); int vips_streamo_putc( VipsStreamo *streamo, int ch ); - #define VIPS_STREAMO_PUTC( S, C ) ( \ (S)->write_point <= VIPS_STREAMO_BUFFER_SIZE ? \ ((S)->output_buffer[(S)->write_point++] = (C), 0) : \ vips_streamo_putc( (S), (C) ) \ ) - +int vips_streamo_writes( VipsStreamo *streamo, const char *str ); int vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... ) __attribute__((format(printf, 2, 3))); diff --git a/libvips/include/vips/util.h b/libvips/include/vips/util.h index dfe18ac9..61e09837 100644 --- a/libvips/include/vips/util.h +++ b/libvips/include/vips/util.h @@ -344,6 +344,8 @@ guint32 vips__random_add( guint32 seed, int value ); const char *vips__icc_dir( void ); const char *vips__windows_prefix( void ); +char *vips__get_iso8601( void ); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/iofuncs/streamib.c b/libvips/iofuncs/streamib.c index bc4730a4..24319ce9 100644 --- a/libvips/iofuncs/streamib.c +++ b/libvips/iofuncs/streamib.c @@ -47,6 +47,7 @@ #include #include +#include #include #ifdef HAVE_UNISTD_H #include @@ -326,11 +327,11 @@ vips_streamib_require( VipsStreamib *streamib, int require ) * slower vips_streamib_get_line_copy(). * * The return value is owned by @streamib and must not be freed. It - * is valid until the next call to vips_streamib_get_line(). + * is valid until the next get call @streamib. * - * Returns: the next line from the file, or NULL on EOF or read error. + * Returns: the next line of text, or NULL on EOF or read error. */ -const unsigned char * +const char * vips_streamib_get_line( VipsStreamib *streamib ) { int write_point; @@ -379,7 +380,7 @@ vips_streamib_get_line( VipsStreamib *streamib ) VIPS_DEBUG_MSG( " %s\n", streamib->line ); - return( streamib->line ); + return( (const char *) streamib->line ); } /** @@ -395,9 +396,9 @@ vips_streamib_get_line( VipsStreamib *streamib ) * This is slower than vips_streamib_get_line(), but can work with lines of * any length. * - * Returns: the next line from the file, or NULL on EOF or read error. + * Returns: the next line of text, or NULL on EOF or read error. */ -unsigned char * +char * vips_streamib_get_line_copy( VipsStreamib *streamib ) { static const unsigned char null = '\0'; @@ -406,7 +407,7 @@ vips_streamib_get_line_copy( VipsStreamib *streamib ) GByteArray *buffer; int ch; - unsigned char *result; + char *result; buffer = g_byte_array_new(); @@ -435,9 +436,87 @@ vips_streamib_get_line_copy( VipsStreamib *streamib ) g_byte_array_append( buffer, &null, 1 ); - result = (unsigned char *) g_byte_array_free( buffer, FALSE ); + result = (char *) g_byte_array_free( buffer, FALSE ); VIPS_DEBUG_MSG( " %s\n", result ); return( result ); } + +/** + * vips_streamib_get_non_whitespace: + * @streamib: stream to operate on + * + * Fetch the next chunk of non-whitespace text from the stream, and + * null-terminate it. + * + * After this, the next getc will be the first char of the next block of + * whitespace (or EOF). + * + * If the first getc is whitespace, stop instantly and return the empty + * string. + * + * If the item is longer than some arbitrary (but large) limit, it is + * truncated. + * + * The return value is owned by @streamib and must not be freed. It + * is valid until the next get call @streamib. + * + * Returns: the next block of non-whitespace, or NULL on EOF or read error. + */ +const char * +vips_streamib_get_non_whitespace( VipsStreamib *streamib ) +{ + int ch; + int i; + + for( i = 0; i < VIPS_STREAMIB_BUFFER_SIZE && + !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && + ch != EOF; i++ ) + streamib->line[i] = ch; + streamib->line[i] = '\0'; + + /* If we stopped before seeing any whitespace, skip to the end of the + * block of non-whitespace. + */ + if( !isspace( ch ) ) + while( !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && + ch != EOF ) + ; + + /* If we finally stopped on whitespace, step back one so the next get + * will be whitespace (or EOF). + */ + if( isspace( ch ) ) + VIPS_STREAMIB_UNGETC( streamib ); + + return( (const char *) streamib->line ); +} + +/** + * vips_streamib_skip_whitespace: + * @streamib: stream to operate on + * + * After this, the next getc will be the first char of the next block of + * non-whitespace (or EOF). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_streamib_skip_whitespace( VipsStreamib *streamib ) +{ + int ch; + + while( isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) ) + ; + VIPS_STREAMIB_UNGETC( streamib ); + + /* # skip comments too. + */ + if( ch == '#' && + (!vips_streamib_get_line( streamib ) || + vips_streamib_skip_whitespace( streamib )) ) + return( -1 ); + + return( 0 ); +} diff --git a/libvips/iofuncs/streamo.c b/libvips/iofuncs/streamo.c index 7c0d62dd..57ca95c3 100644 --- a/libvips/iofuncs/streamo.c +++ b/libvips/iofuncs/streamo.c @@ -402,35 +402,6 @@ vips_streamo_finish( VipsStreamo *streamo ) class->finish( streamo ); } -/** - * vips_streamo_writef: - * @streamo: output stream to operate on - * @fmt: printf()-style format string - * @...: arguments to format string - * - * Format the string and write to @streamo. - * - * Returns: 0 on success, and -1 on error. - */ -int -vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... ) -{ - va_list ap; - char *line; - int result; - - va_start( ap, fmt ); - line = g_strdup_vprintf( fmt, ap ); - va_end( ap ); - - result = vips_streamo_write( streamo, - (unsigned char *) line, strlen( line ) ); - - g_free( line ); - - return( result ); -} - /** * vips_streamo_putc: * @streamo: output stream to operate on @@ -455,6 +426,50 @@ vips_streamo_putc( VipsStreamo *streamo, int ch ) return( 0 ); } +/** + * vips_streamo_writes: + * @streamo: output stream to operate on + * @str: string to write + * + * Write a null-terminated string to @streamo. + * + * Returns: 0 on success, and -1 on error. + */ +int +vips_streamo_writes( VipsStreamo *streamo, const char *str ) +{ + return( vips_streamo_write( streamo, + (unsigned char *) str, strlen( str ) ) ); +} + +/** + * vips_streamo_writef: + * @streamo: output stream to operate on + * @fmt: printf()-style format string + * @...: arguments to format string + * + * Format the string and write to @streamo. + * + * Returns: 0 on success, and -1 on error. + */ +int +vips_streamo_writef( VipsStreamo *streamo, const char *fmt, ... ) +{ + va_list ap; + char *line; + int result; + + va_start( ap, fmt ); + line = g_strdup_vprintf( fmt, ap ); + va_end( ap ); + + result = vips_streamo_writes( streamo, line ); + + g_free( line ); + + return( result ); +} + /** * vips_streamo_write_amp: * @streamo: output stream to operate on diff --git a/libvips/iofuncs/util.c b/libvips/iofuncs/util.c index b334a972..80d11403 100644 --- a/libvips/iofuncs/util.c +++ b/libvips/iofuncs/util.c @@ -2024,3 +2024,28 @@ vips__windows_prefix( void ) return( (const char *) g_once( &once, (GThreadFunc) vips__windows_prefix_once, NULL ) ); } + +char * +vips__get_iso8601( void ) +{ + char *date; + +#ifdef HAVE_DATE_TIME_FORMAT_ISO8601 +{ + GDateTime *now; + + now = g_date_time_new_now_local(); + date = g_date_time_format_iso8601( now ); + g_date_time_unref( now ); +} +#else /*!HAVE_DATE_TIME_FORMAT_ISO8601*/ +{ + GTimeVal now; + + g_get_current_time( &now ); + date = g_time_val_to_iso8601( &now ); +} +#endif /*HAVE_DATE_TIME_FORMAT_ISO8601*/ + + return( date ); +} diff --git a/libvips/iofuncs/vips.c b/libvips/iofuncs/vips.c index 26d43687..b7f40c10 100644 --- a/libvips/iofuncs/vips.c +++ b/libvips/iofuncs/vips.c @@ -957,22 +957,7 @@ vips__xml_properties( VipsImage *image ) VipsDbuf dbuf; char *date; -#ifdef HAVE_DATE_TIME_FORMAT_ISO8601 -{ - GDateTime *now; - - now = g_date_time_new_now_local(); - date = g_date_time_format_iso8601( now ); - g_date_time_unref( now ); -} -#else /*!HAVE_DATE_TIME_FORMAT_ISO8601*/ -{ - GTimeVal now; - - g_get_current_time( &now ); - date = g_time_val_to_iso8601( &now ); -} -#endif /*HAVE_DATE_TIME_FORMAT_ISO8601*/ + date = vips__get_iso8601(); vips_dbuf_init( &dbuf ); vips_dbuf_writef( &dbuf, "\n" );