diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 4b90557f..9d8ca8c5 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -527,6 +527,8 @@ vips_foreign_find_load( const char *name ) vips__filename_split8( name, filename, option_string ); + /* Very common, so make a better error message for this case. + */ if( !vips_existsf( "%s", filename ) ) { vips_error( "VipsForeignLoad", _( "file \"%s\" not found" ), name ); @@ -1990,7 +1992,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_rad_buffer_get_type( 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_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_load_png_get_type( void ); extern GType vips_foreign_load_png_buffer_get_type( void ); @@ -2074,7 +2076,7 @@ vips_foreign_operation_init( void ) #endif /*HAVE_ANALYZE*/ #ifdef HAVE_PPM - vips_foreign_load_ppm_get_type(); + vips_foreign_load_ppm_file_get_type(); vips_foreign_save_ppm_get_type(); #endif /*HAVE_PPM*/ diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 90693685..cafb4bab 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -143,10 +143,6 @@ int vips__mat_load( const char *filename, VipsImage *out ); int vips__mat_header( const char *filename, VipsImage *out ); int vips__mat_ismat( const char *filename ); -int vips__ppm_header_stream( VipsStreamib *streamib, VipsImage *out ); -int vips__ppm_load_stream( VipsStreamib *streamib, VipsImage *out ); -VipsForeignFlags vips__ppm_flags_stream( VipsStreamib *streamib ); -int vips__ppm_isppm_stream( VipsStreami *streami ); extern const char *vips__ppm_suffs[]; int vips__ppm_save_stream( VipsImage *in, VipsStreamo *streamo, diff --git a/libvips/foreign/ppm.c b/libvips/foreign/ppm.c index 582ac122..d8450d86 100644 --- a/libvips/foreign/ppm.c +++ b/libvips/foreign/ppm.c @@ -1,41 +1,9 @@ /* Read a ppm file. * - * Stephen Chan ... original code - * - * 21/11/00 JC - * - hacked for VIPS - * - reads ppm/pgm/pbm - * - mmaps binary pgm/ppm - * - reads all ascii formats (slowly!) - * 22/11/00 JC - * - oops, ascii read was broken - * - does 16/32 bit ascii now as well - * 24/5/01 - * - im_ppm2vips_header() added - * 28/11/03 JC - * - better no-overshoot on tile loop - * 22/5/04 - * - does 16/32 bit binary too - * - tiny fix for missing file close on read error - * 19/8/05 - * - use im_raw2vips() for binary read - * 9/9/05 - * - tiny cleanups - * 3/11/07 - * - use im_wbuffer() for bg writes + * 4/2/10 * - gtkdoc - * 1/5/10 - * - add PFM (portable float map) support - * 19/12/11 - * - rework as a set of fns ready to be called from a class - * 8/11/14 - * - add 1 bit write - * 4/6/15 - * - try to support DOS files under linux ... we have to look for \r\n - * linebreaks - * 29/7/19 Kyle-Kyle - * - fix a loop with malformed ppm + * 13/11/19 * - redone with streams */ @@ -86,500 +54,6 @@ #include "pforeign.h" -/* 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 void -skip_line( VipsStreamib *streamib ) -{ - int ch; - - while( (ch = VIPS_STREAMIB_GETC( streamib )) != '\n' && - ch != EOF ) - ; -} - -/* After this, the next getc will be the first char of the next block of - * non-whitespace (or EOF). - */ -static void -skip_white_space( VipsStreamib *streamib ) -{ - int ch; - - while( isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) ) - ; - VIPS_STREAMIB_UNGETC( streamib ); - - if( ch == '#' ) { - skip_line( streamib ); - skip_white_space( streamib ); - } -} - -/* 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 void -read_non_white_space( VipsStreamib *streamib, char *buf, int length ) -{ - int ch; - int i; - - for( i = 0; i < length - 1 && - !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && - ch != EOF; i++ ) - buf[i] = ch; - buf[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 ); -} - -static int -read_int( VipsStreamib *streamib, int *i ) -{ - char buf[MAX_THING]; - - skip_white_space( streamib ); - read_non_white_space( streamib, buf, MAX_THING ); - - if( sscanf( buf, "%d", i ) != 1 ) { - vips_error( "ppm2vips", "%s", _( "bad int" ) ); - return( -1 ); - } - - return( 0 ); -} - -static int -read_float( VipsStreamib *streamib, float *f ) -{ - char buf[MAX_THING]; - - skip_white_space( streamib ); - read_non_white_space( streamib, buf, MAX_THING ); - - if( sscanf( buf, "%f", f ) != 1 ) { - vips_error( "ppm2vips", "%s", _( "bad float" ) ); - return( -1 ); - } - - return( 0 ); -} - -/* ppm types. - */ -static char *magic_names[] = { - "P1", /* pbm ... 1 band 1 bit, ascii */ - "P2", /* pgm ... 1 band many bit, ascii */ - "P3", /* ppm ... 3 band many bit, ascii */ - "P4", /* pbm ... 1 band 1 bit, binary */ - "P5", /* pgm ... 1 band 8 bit, binary */ - "P6", /* ppm ... 3 band 8 bit, binary */ - "PF", /* pfm ... 3 band 32 bit, binary */ - "Pf" /* pfm ... 1 band 32 bit, binary */ -}; - -static int -read_header( VipsStreamib *streamib, VipsImage *out, - int *bits, int *ascii, int *msb_first ) -{ - int width, height, bands; - VipsBandFormat format; - VipsInterpretation interpretation; - int index; - char buf[MAX_THING]; - - /* Characteristics, indexed by ppm type. - */ - static int lookup_bits[] = { - 1, 8, 8, 1, 8, 8, 32, 32 - }; - static int lookup_bands[] = { - 1, 1, 3, 1, 1, 3, 3, 1 - }; - static int lookup_ascii[] = { - 1, 1, 1, 0, 0, 0, 0, 0 - }; - - /* Read in the magic number. - */ - buf[0] = VIPS_STREAMIB_GETC( streamib ); - buf[1] = VIPS_STREAMIB_GETC( streamib ); - buf[2] = '\0'; - - for( index = 0; index < VIPS_NUMBER( magic_names ); index++ ) - if( strcmp( magic_names[index], buf ) == 0 ) - break; - if( index == VIPS_NUMBER( magic_names ) ) { - vips_error( "ppm2vips", "%s", _( "bad magic number" ) ); - return( -1 ); - } - *bits = lookup_bits[index]; - bands = lookup_bands[index]; - *ascii = lookup_ascii[index]; - - /* Default ... can be changed below for PFM images. - */ - *msb_first = 0; - - /* Read in size. - */ - if( read_int( streamib, &width ) || - read_int( streamib, &height ) ) - return( -1 ); - - /* Read in max value / scale for >1 bit images. - */ - if( *bits > 1 ) { - if( index == 6 || index == 7 ) { - float scale; - - if( read_float( streamib, &scale ) ) - return( -1 ); - - /* Scale > 0 means big-endian. - */ - *msb_first = scale > 0; - vips_image_set_double( out, - "pfm-scale", VIPS_FABS( scale ) ); - } - else { - int max_value; - - if( read_int( streamib, &max_value ) ) - return( -1 ); - - if( max_value > 255 ) - *bits = 16; - if( max_value > 65535 ) - *bits = 32; - } - } - - /* For binary images, there is always exactly 1 more whitespace - * character before the data starts. - */ - if( !*ascii && - !isspace( VIPS_STREAMIB_GETC( streamib ) ) ) { - vips_error( "ppm2vips", "%s", - _( "not whitespace before start of binary data" ) ); - return( -1 ); - } - - /* Choose a VIPS bandfmt. - */ - switch( *bits ) { - case 1: - case 8: - format = VIPS_FORMAT_UCHAR; - break; - - case 16: - format = VIPS_FORMAT_USHORT; - break; - - case 32: - if( index == 6 || index == 7 ) - format = VIPS_FORMAT_FLOAT; - else - format = VIPS_FORMAT_UINT; - break; - - default: - g_assert_not_reached(); - - /* Stop compiler warnings. - */ - format = VIPS_FORMAT_UCHAR; - } - - if( bands == 1 ) { - if( format == VIPS_FORMAT_USHORT ) - interpretation = VIPS_INTERPRETATION_GREY16; - else - interpretation = VIPS_INTERPRETATION_B_W; - } - else { - if( format == VIPS_FORMAT_USHORT ) - interpretation = VIPS_INTERPRETATION_RGB16; - else if( format == VIPS_FORMAT_UINT ) - interpretation = VIPS_INTERPRETATION_RGB; - else - interpretation = VIPS_INTERPRETATION_sRGB; - } - - vips_image_init_fields( out, - width, height, bands, format, - VIPS_CODING_NONE, interpretation, 1.0, 1.0 ); - - VIPS_SETSTR( out->filename, - vips_stream_filename( VIPS_STREAM( streamib->streami ) ) ); - - return( 0 ); -} - -static void -read_mmap_close_cb( VipsObject *object, VipsStreamib *streamib ) -{ - VIPS_UNREF( streamib ); -} - -/* Read a ppm/pgm file using mmap(). - */ -static int -read_mmap( VipsStreamib *streamib, int msb_first, VipsImage *out ) -{ - VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( out ), 3 ); - VipsStreami *streami = streamib->streami; - - gint64 header_offset; - size_t length; - const void *base_addr; - - vips_streamib_unbuffer( streamib ); - if( (header_offset = vips_streami_seek( streami, 0, SEEK_CUR )) < 0 || - !(base_addr = vips_streami_map( streami, &length )) ) - return( -1 ); - - if( !(t[0] = vips_image_new_from_memory( - base_addr + header_offset, length - header_offset, - out->Xsize, out->Ysize, out->Bands, out->BandFmt )) || - vips_copy( t[0], &t[1], - "interpretation", out->Type, - NULL ) || - vips__byteswap_bool( t[1], &t[2], - vips_amiMSBfirst() != msb_first ) || - vips_image_write( t[2], out ) ) - return( -1 ); - - /* out is using memory from the streami mmap. We need to keep a ref - * until out is closed. - */ - g_object_ref( streamib ); - g_signal_connect( out, "close", - G_CALLBACK( read_mmap_close_cb ), streamib ); - - return( 0 ); -} - -/* Read an ascii ppm/pgm file. - */ -static int -read_ascii( VipsStreamib *streamib, VipsImage *out ) -{ - int x, y; - VipsPel *buf; - - if( !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), VipsPel )) ) - return( -1 ); - - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++ ) { - int val; - - if( read_int( streamib, &val ) ) - return( -1 ); - - switch( out->BandFmt ) { - case VIPS_FORMAT_UCHAR: - buf[x] = VIPS_CLIP( 0, val, 255 ); - break; - - case VIPS_FORMAT_USHORT: - ((unsigned short *) buf)[x] = - VIPS_CLIP( 0, val, 65535 ); - break; - - case VIPS_FORMAT_UINT: - ((unsigned int *) buf)[x] = val; - break; - - default: - g_assert_not_reached(); - } - } - - if( vips_image_write_line( out, y, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -/* Read an ascii 1 bit file. - */ -static int -read_1bit_ascii( VipsStreamib *streamib, VipsImage *out ) -{ - int x, y; - VipsPel *buf; - - if( !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), VipsPel )) ) - return( -1 ); - - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++ ) { - int val; - - if( read_int( streamib, &val ) ) - return( -1 ); - - if( val ) - buf[x] = 0; - else - buf[x] = 255; - } - - if( vips_image_write_line( out, y, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -/* Read a 1 bit binary file. - */ -static int -read_1bit_binary( VipsStreamib *streamib, VipsImage *out ) -{ - int x, y; - int bits; - VipsPel *buf; - - if( !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), VipsPel )) ) - return( -1 ); - - bits = VIPS_STREAMIB_GETC( streamib ); - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++ ) { - buf[x] = (bits & 128) ? 0 : 255; - bits = VIPS_LSHIFT_INT( bits, 1 ); - if( (x & 7) == 7 ) - bits = VIPS_STREAMIB_GETC( streamib ); - } - - /* Skip any unaligned bits at end of line. - */ - if( (x & 7) != 0 ) - bits = VIPS_STREAMIB_GETC( streamib ); - - if( vips_image_write_line( out, y, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -int -vips__ppm_header_stream( VipsStreamib *streamib, VipsImage *out ) -{ - int bits; - int ascii; - int msb_first; - - if( read_header( streamib, out, &bits, &ascii, &msb_first ) ) - return( -1 ); - - return( 0 ); -} - -int -vips__ppm_load_stream( VipsStreamib *streamib, VipsImage *out ) -{ - int bits; - int ascii; - int msb_first; - - if( read_header( streamib, out, &bits, &ascii, &msb_first ) ) - return( -1 ); - - /* What sort of read are we doing? - */ - if( !ascii && bits >= 8 ) - return( read_mmap( streamib, msb_first, out ) ); - else if( !ascii && bits == 1 ) - return( read_1bit_binary( streamib, out ) ); - else if( ascii && bits == 1 ) - return( read_1bit_ascii( streamib, out ) ); - else - return( read_ascii( streamib, out ) ); - - return( 0 ); -} - -/* Can this PPM file be read with a mmap? - */ -static int -isppmmmap( VipsStreamib *streamib ) -{ - VipsImage *im; - int bits; - int ascii; - int msb_first; - - im = vips_image_new(); - if( read_header( streamib, im, &bits, &ascii, &msb_first ) ) { - g_object_unref( im ); - - return( 0 ); - } - g_object_unref( im ); - - return( !ascii && bits >= 8 ); -} - -/* ppm flags function. - */ -VipsForeignFlags -vips__ppm_flags_stream( VipsStreamib *streamib ) -{ - VipsForeignFlags flags; - - flags = 0; - if( isppmmmap( streamib ) ) - flags |= VIPS_FOREIGN_PARTIAL; - - return( flags ); -} - -int -vips__ppm_isppm_stream( VipsStreami *streami ) -{ - const unsigned char *data; - - if( (data = vips_streami_sniff( streami, 2 )) ) { - int i; - - for( i = 0; i < VIPS_NUMBER( magic_names ); i++ ) - if( vips_isprefix( magic_names[i], (char *) data ) ) - return( TRUE ); - } - - return( 0 ); -} - -const char *vips__ppm_suffs[] = { ".ppm", ".pgm", ".pbm", ".pfm", NULL }; - struct _Write; typedef int (*write_fn)( struct _Write *write, VipsPel *p ); diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c index 862ee780..c2d08d34 100644 --- a/libvips/foreign/ppmload.c +++ b/libvips/foreign/ppmload.c @@ -1,7 +1,38 @@ /* load ppm from a file * - * 5/12/11 - * - from tiffload.c + * Stephen Chan ... original code + * + * 21/11/00 JC + * - hacked for VIPS + * - reads ppm/pgm/pbm + * - mmaps binary pgm/ppm + * - reads all ascii formats (slowly!) + * 22/11/00 JC + * - oops, ascii read was broken + * - does 16/32 bit ascii now as well + * 24/5/01 + * - im_ppm2vips_header() added + * 28/11/03 JC + * - better no-overshoot on tile loop + * 22/5/04 + * - does 16/32 bit binary too + * - tiny fix for missing file close on read error + * 19/8/05 + * - use im_raw2vips() for binary read + * 9/9/05 + * - tiny cleanups + * 3/11/07 + * - use im_wbuffer() for bg writes + * 1/5/10 + * - add PFM (portable float map) support + * 19/12/11 + * - rework as a set of fns ready to be called from a class + * 8/11/14 + * - add 1 bit write + * 29/7/19 Kyle-Kyle + * - fix a loop with malformed ppm + * 13/11/19 + * - redone with streams */ /* @@ -40,12 +71,12 @@ #endif /*HAVE_CONFIG_H*/ #include +#include #include #include #include #include -#include #include #include "pforeign.h" @@ -55,48 +86,315 @@ typedef struct _VipsForeignLoadPpm { VipsForeignLoad parent_object; - /* Filename for load. + /* The stream we load from, and the buffered wrapper. */ - char *filename; + VipsStreami *streami; + VipsStreamib *streamib; + + /* Properties of this ppm, from the header. + */ + int width; + int height; + int bands; + VipsBandFormat format; + VipsInterpretation interpretation; + float scale; + int max_value; + int index; /* ppm type .. index in magic_names[] */ + int bits; /* 1, 8 or 32 */ + gboolean ascii; /* TRUE for ascii encoding */ + gboolean msb_first; /* TRUE if most sig byte is first */ + + gboolean have_read_header; } VipsForeignLoadPpm; typedef VipsForeignLoadClass VipsForeignLoadPpmClass; -G_DEFINE_TYPE( VipsForeignLoadPpm, vips_foreign_load_ppm, +G_DEFINE_ABSTRACT_TYPE( VipsForeignLoadPpm, vips_foreign_load_ppm, VIPS_TYPE_FOREIGN_LOAD ); +/* ppm types. + */ +static char *magic_names[] = { + "P1", /* pbm ... 1 band 1 bit, ascii */ + "P2", /* pgm ... 1 band many bit, ascii */ + "P3", /* ppm ... 3 band many bit, ascii */ + "P4", /* pbm ... 1 band 1 bit, binary */ + "P5", /* pgm ... 1 band 8 bit, binary */ + "P6", /* ppm ... 3 band 8 bit, binary */ + "PF", /* pfm ... 3 band 32 bit, binary */ + "Pf" /* pfm ... 1 band 32 bit, binary */ +}; + +/* Shared with ppmsave. + */ +const char *vips__ppm_suffs[] = { ".ppm", ".pgm", ".pbm", ".pfm", NULL }; + static gboolean -vips_foreign_load_ppm_is_a( const char *filename ) +vips_foreign_load_ppm_is_a_stream( VipsStreami *streami ) { - VipsStreami *streami; - gboolean result; + const unsigned char *data; - if( !(streami = vips_streami_new_from_filename( filename )) ) - return( FALSE ); - result = vips__ppm_isppm_stream( streami ); - VIPS_UNREF( streami ); + if( (data = vips_streami_sniff( streami, 2 )) ) { + int i; - return( result ); + for( i = 0; i < VIPS_NUMBER( magic_names ); i++ ) + if( vips_isprefix( magic_names[i], (char *) data ) ) + return( TRUE ); + } + + return( FALSE ); } -static VipsForeignFlags -vips_foreign_load_ppm_get_flags_filename( const char *filename ) +/* 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 ) { - VipsStreami *streami; - VipsStreamib *streamib; - VipsForeignFlags flags; + int ch; - if( !(streami = vips_streami_new_from_filename( filename )) ) - return( 0 ); - streamib = vips_streamib_new( streami ); - VIPS_UNREF( streami ); + while( (ch = VIPS_STREAMIB_GETC( streamib )) != '\n' && + ch != EOF ) + ; - flags = vips__ppm_flags_stream( streamib ); + if( ch == EOF ) + return( -1 ); - VIPS_UNREF( streamib ); + return( 0 ); +} - return( flags ); +/* 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 ) +{ + int ch; + int i; + + for( i = 0; i < length - 1 && + !isspace( ch = VIPS_STREAMIB_GETC( streamib ) ) && + ch != EOF; i++ ) + buf[i] = ch; + buf[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( 0 ); +} + +static int +read_int( VipsStreamib *streamib, int *i ) +{ + char buf[MAX_THING]; + + if( skip_white_space( 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 ); + + return( 0 ); +} + +static void +vips_foreign_load_ppm_dispose( GObject *gobject ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) gobject; + + VIPS_UNREF( ppm->streamib ); + VIPS_UNREF( ppm->streami ); + + G_OBJECT_CLASS( vips_foreign_load_ppm_parent_class )-> + dispose( gobject ); +} + +/* Scan the stream header into our class. + */ +static int +vips_foreign_load_ppm_parse_header( VipsForeignLoadPpm *ppm ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( ppm ); + + int i; + char buf[2]; + + /* Characteristics, indexed by ppm type. + */ + static int lookup_bits[] = { + 1, 8, 8, 1, 8, 8, 32, 32 + }; + static int lookup_bands[] = { + 1, 1, 3, 1, 1, 3, 3, 1 + }; + static int lookup_ascii[] = { + 1, 1, 1, 0, 0, 0, 0, 0 + }; + + /* Read in the magic number. + */ + buf[0] = VIPS_STREAMIB_GETC( ppm->streamib ); + buf[1] = VIPS_STREAMIB_GETC( ppm->streamib ); + + for( i = 0; i < VIPS_NUMBER( magic_names ); i++ ) + if( vips_isprefix( magic_names[i], buf ) ) + break; + if( i == VIPS_NUMBER( magic_names ) ) { + vips_error( class->nickname, "%s", _( "bad magic number" ) ); + return( -1 ); + } + ppm->index = i; + ppm->bits = lookup_bits[i]; + ppm->bands = lookup_bands[i]; + ppm->ascii = lookup_ascii[i]; + + /* Default ... can be changed below for PFM images. + */ + ppm->msb_first = 0; + + /* Read in size. + */ + if( read_int( ppm->streamib, &ppm->width ) || + read_int( ppm->streamib, &ppm->height ) ) + return( -1 ); + + /* Read in max value / scale for >1 bit images. + */ + if( ppm->bits > 1 ) { + if( ppm->index == 6 || + ppm->index == 7 ) { + if( read_float( ppm->streamib, &ppm->scale ) ) + return( -1 ); + + /* Scale > 0 means big-endian. + */ + ppm->msb_first = ppm->scale > 0; + } + else { + if( read_int( ppm->streamib, &ppm->max_value ) ) + return( -1 ); + + if( ppm->max_value > 255 ) + ppm->bits = 16; + if( ppm->max_value > 65535 ) + ppm->bits = 32; + } + } + + /* For binary images, there is always exactly 1 more whitespace + * character before the data starts. + */ + if( !ppm->ascii && + !isspace( VIPS_STREAMIB_GETC( ppm->streamib ) ) ) { + vips_error( class->nickname, "%s", + _( "no whitespace before start of binary data" ) ); + return( -1 ); + } + + /* Choose a VIPS bandfmt. + */ + switch( ppm->bits ) { + case 1: + case 8: + ppm->format = VIPS_FORMAT_UCHAR; + break; + + case 16: + ppm->format = VIPS_FORMAT_USHORT; + break; + + case 32: + if( ppm->index == 6 || + ppm->index == 7 ) + ppm->format = VIPS_FORMAT_FLOAT; + else + ppm->format = VIPS_FORMAT_UINT; + break; + + default: + g_assert_not_reached(); + + /* Stop compiler warnings. + */ + ppm->format = VIPS_FORMAT_UCHAR; + } + + if( ppm->bands == 1 ) { + if( ppm->format == VIPS_FORMAT_USHORT ) + ppm->interpretation = VIPS_INTERPRETATION_GREY16; + else + ppm->interpretation = VIPS_INTERPRETATION_B_W; + } + else { + if( ppm->format == VIPS_FORMAT_USHORT ) + ppm->interpretation = VIPS_INTERPRETATION_RGB16; + else if( ppm->format == VIPS_FORMAT_UINT ) + ppm->interpretation = VIPS_INTERPRETATION_RGB; + else + ppm->interpretation = VIPS_INTERPRETATION_sRGB; + } + + ppm->have_read_header = TRUE; + + return( 0 ); } static VipsForeignFlags @@ -104,7 +402,36 @@ vips_foreign_load_ppm_get_flags( VipsForeignLoad *load ) { VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; - return( vips_foreign_load_ppm_get_flags_filename( ppm->filename ) ); + VipsForeignFlags flags; + + if( !ppm->have_read_header && + vips_foreign_load_ppm_parse_header( ppm ) ) + return( 0 ); + + flags = 0; + if( !ppm->ascii && ppm->bits >= 8 ) + flags |= VIPS_FOREIGN_PARTIAL; + + return( flags ); +} + +static void +vips_foreign_load_ppm_set_image( VipsForeignLoadPpm *ppm, VipsImage *image ) +{ + vips_image_init_fields( image, + ppm->width, ppm->height, ppm->bands, ppm->format, + VIPS_CODING_NONE, ppm->interpretation, 1.0, 1.0 ); + + if( ppm->index == 6 || + ppm->index == 7 ) + vips_image_set_double( image, + "pfm-scale", VIPS_FABS( ppm->scale ) ); + else + vips_image_set_double( image, + "ppm-max-value", VIPS_FABS( ppm->max_value ) ); + + VIPS_SETSTR( image->filename, + vips_stream_filename( VIPS_STREAM( ppm->streamib->streami ) ) ); } static int @@ -112,43 +439,189 @@ vips_foreign_load_ppm_header( VipsForeignLoad *load ) { VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; - VipsStreami *streami; - VipsStreamib *streamib; + if( !ppm->have_read_header && + vips_foreign_load_ppm_parse_header( ppm ) ) + return( 0 ); - if( !(streami = vips_streami_new_from_filename( ppm->filename )) ) - return( -1 ); - streamib = vips_streamib_new( streami ); - VIPS_UNREF( streami ); - - if( vips__ppm_header_stream( streamib, load->out ) ) { - VIPS_UNREF( streamib ); - return( -1 ); - } - - VIPS_UNREF( streamib ); + vips_foreign_load_ppm_set_image( ppm, load->out ); return( 0 ); } +/* Read a ppm/pgm file using mmap(). + */ +static int +load_mmap( VipsForeignLoadPpm *ppm, VipsImage *image ) +{ + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( ppm ), 3 ); + + gint64 header_offset; + size_t length; + const void *data; + + vips_streamib_unbuffer( ppm->streamib ); + header_offset = vips_streami_seek( ppm->streami, 0, SEEK_CUR ); + data = vips_streami_map( ppm->streami, &length ); + if( header_offset < 0 || !data ) + return( -1 ); + data += header_offset; + length -= header_offset; + + if( !(t[0] = vips_image_new_from_memory( data, length, + ppm->width, ppm->height, ppm->bands, ppm->format )) ) + return( -1 ); + vips_foreign_load_ppm_set_image( ppm, t[0] ); + + if( vips__byteswap_bool( t[0], &t[1], + vips_amiMSBfirst() != ppm->msb_first ) || + vips_image_write( t[1], image ) ) + return( -1 ); + + return( 0 ); +} + +/* Read an ascii 1 bit file. + */ +static int +load_1bit_ascii( VipsForeignLoadPpm *ppm, VipsImage *image ) +{ + int x, y; + VipsPel *buf; + + vips_foreign_load_ppm_set_image( ppm, image ); + + if( !(buf = VIPS_ARRAY( image, + VIPS_IMAGE_SIZEOF_LINE( image ), VipsPel )) ) + return( -1 ); + + for( y = 0; y < image->Ysize; y++ ) { + for( x = 0; x < image->Xsize; x++ ) { + int val; + + if( read_int( ppm->streamib, &val ) ) + return( -1 ); + + if( val ) + buf[x] = 0; + else + buf[x] = 255; + } + + if( vips_image_write_line( image, y, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a 1 bit binary file. + */ +static int +load_1bit_binary( VipsForeignLoadPpm *ppm, VipsImage *image ) +{ + int x, y; + int bits; + VipsPel *buf; + + vips_foreign_load_ppm_set_image( ppm, image ); + + if( !(buf = VIPS_ARRAY( image, + VIPS_IMAGE_SIZEOF_LINE( image ), VipsPel )) ) + return( -1 ); + + bits = VIPS_STREAMIB_GETC( ppm->streamib ); + for( y = 0; y < image->Ysize; y++ ) { + for( x = 0; x < image->Xsize; x++ ) { + buf[x] = (bits & 128) ? 0 : 255; + bits = VIPS_LSHIFT_INT( bits, 1 ); + if( (x & 7) == 7 ) + bits = VIPS_STREAMIB_GETC( ppm->streamib ); + } + + /* Skip any unaligned bits at end of line. + */ + if( (x & 7) != 0 ) + bits = VIPS_STREAMIB_GETC( ppm->streamib ); + + if( vips_image_write_line( image, y, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read an ascii ppm/pgm file. + */ +static int +load_ascii( VipsForeignLoadPpm *ppm, VipsImage *image ) +{ + int x, y; + VipsPel *buf; + + if( !(buf = + VIPS_ARRAY( image, VIPS_IMAGE_SIZEOF_LINE( image ), VipsPel )) ) + return( -1 ); + + for( y = 0; y < image->Ysize; y++ ) { + for( x = 0; x < image->Xsize * image->Bands; x++ ) { + int val; + + if( read_int( ppm->streamib, &val ) ) + return( -1 ); + + switch( image->BandFmt ) { + case VIPS_FORMAT_UCHAR: + buf[x] = VIPS_CLIP( 0, val, 255 ); + break; + + case VIPS_FORMAT_USHORT: + ((unsigned short *) buf)[x] = + VIPS_CLIP( 0, val, 65535 ); + break; + + case VIPS_FORMAT_UINT: + ((unsigned int *) buf)[x] = val; + break; + + default: + g_assert_not_reached(); + } + } + + if( vips_image_write_line( image, y, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +typedef int (*VipsPpmLoaderFn)( VipsForeignLoadPpm *ppm, VipsImage *image ); + static int vips_foreign_load_ppm_load( VipsForeignLoad *load ) { VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; - VipsStreami *streami; - VipsStreamib *streamib; + VipsPpmLoaderFn loader; - if( !(streami = vips_streami_new_from_filename( ppm->filename )) ) + if( !ppm->have_read_header && + vips_foreign_load_ppm_parse_header( ppm ) ) + return( 0 ); + + /* What sort of read are we doing? + */ + if( !ppm->ascii && ppm->bits >= 8 ) + loader = load_mmap; + else if( !ppm->ascii && ppm->bits == 1 ) + loader = load_1bit_binary; + else if( ppm->ascii && ppm->bits == 1 ) + loader = load_1bit_ascii; + else + loader = load_ascii; + + if( loader( ppm, load->real ) ) return( -1 ); - streamib = vips_streamib_new( streami ); - VIPS_UNREF( streami ); - - if( vips__ppm_load_stream( streamib, load->real ) ) { - VIPS_UNREF( streamib ); - return( -1 ); - } - - VIPS_UNREF( streamib ); return( 0 ); } @@ -161,11 +634,12 @@ vips_foreign_load_ppm_class_init( VipsForeignLoadPpmClass *class ) VipsForeignClass *foreign_class = (VipsForeignClass *) class; VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + gobject_class->dispose = vips_foreign_load_ppm_dispose; gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "ppmload"; - object_class->description = _( "load ppm from file" ); + object_class->nickname = "ppmload_base"; + object_class->description = _( "load ppm base class" ); foreign_class->suffs = vips__ppm_suffs; @@ -173,23 +647,90 @@ vips_foreign_load_ppm_class_init( VipsForeignLoadPpmClass *class ) */ foreign_class->priority = 200; - load_class->is_a = vips_foreign_load_ppm_is_a; - load_class->get_flags_filename = - vips_foreign_load_ppm_get_flags_filename; load_class->get_flags = vips_foreign_load_ppm_get_flags; load_class->header = vips_foreign_load_ppm_header; load_class->load = vips_foreign_load_ppm_load; +} + +static void +vips_foreign_load_ppm_init( VipsForeignLoadPpm *ppm ) +{ + ppm->scale = 1.0; +} + +typedef struct _VipsForeignLoadPpmFile { + VipsForeignLoadPpm parent_object; + + char *filename; + +} VipsForeignLoadPpmFile; + +typedef VipsForeignLoadPpmClass VipsForeignLoadPpmFileClass; + +G_DEFINE_TYPE( VipsForeignLoadPpmFile, vips_foreign_load_ppm_file, + vips_foreign_load_ppm_get_type() ); + +static gboolean +vips_foreign_load_ppm_file_is_a( const char *filename ) +{ + VipsStreami *streami; + gboolean result; + + if( !(streami = vips_streami_new_from_filename( filename )) ) + return( FALSE ); + result = vips_foreign_load_ppm_is_a_stream( streami ); + VIPS_UNREF( streami ); + + return( result ); +} + +static int +vips_foreign_load_ppm_file_build( VipsObject *object ) +{ + VipsForeignLoadPpmFile *file = (VipsForeignLoadPpmFile *) object; + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) object; + + if( file->filename ) { + if( !(ppm->streami = + vips_streami_new_from_filename( file->filename )) ) + return( -1 ); + ppm->streamib = vips_streamib_new( ppm->streami ); + } + + if( VIPS_OBJECT_CLASS( vips_foreign_load_ppm_file_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_ppm_file_class_init( VipsForeignLoadPpmClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) 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 = "ppmload"; + object_class->description = _( "load ppm from file" ); + object_class->build = vips_foreign_load_ppm_file_build; + + load_class->is_a = vips_foreign_load_ppm_file_is_a; + VIPS_ARG_STRING( class, "filename", 1, _( "Filename" ), _( "Filename to load from" ), VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignLoadPpm, filename ), + G_STRUCT_OFFSET( VipsForeignLoadPpmFile, filename ), NULL ); } static void -vips_foreign_load_ppm_init( VipsForeignLoadPpm *ppm ) +vips_foreign_load_ppm_file_init( VipsForeignLoadPpmFile *file ) { } diff --git a/libvips/foreign/ppmload.old.c b/libvips/foreign/ppmload.old.c new file mode 100644 index 00000000..862ee780 --- /dev/null +++ b/libvips/foreign/ppmload.old.c @@ -0,0 +1,226 @@ +/* load ppm from a file + * + * 5/12/11 + * - from tiffload.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 "pforeign.h" + +#ifdef HAVE_PPM + +typedef struct _VipsForeignLoadPpm { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadPpm; + +typedef VipsForeignLoadClass VipsForeignLoadPpmClass; + +G_DEFINE_TYPE( VipsForeignLoadPpm, vips_foreign_load_ppm, + VIPS_TYPE_FOREIGN_LOAD ); + +static gboolean +vips_foreign_load_ppm_is_a( const char *filename ) +{ + VipsStreami *streami; + gboolean result; + + if( !(streami = vips_streami_new_from_filename( filename )) ) + return( FALSE ); + result = vips__ppm_isppm_stream( streami ); + VIPS_UNREF( streami ); + + return( result ); +} + +static VipsForeignFlags +vips_foreign_load_ppm_get_flags_filename( const char *filename ) +{ + VipsStreami *streami; + VipsStreamib *streamib; + VipsForeignFlags flags; + + if( !(streami = vips_streami_new_from_filename( filename )) ) + return( 0 ); + streamib = vips_streamib_new( streami ); + VIPS_UNREF( streami ); + + flags = vips__ppm_flags_stream( streamib ); + + VIPS_UNREF( streamib ); + + return( flags ); +} + +static VipsForeignFlags +vips_foreign_load_ppm_get_flags( VipsForeignLoad *load ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; + + return( vips_foreign_load_ppm_get_flags_filename( ppm->filename ) ); +} + +static int +vips_foreign_load_ppm_header( VipsForeignLoad *load ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; + + VipsStreami *streami; + VipsStreamib *streamib; + + if( !(streami = vips_streami_new_from_filename( ppm->filename )) ) + return( -1 ); + streamib = vips_streamib_new( streami ); + VIPS_UNREF( streami ); + + if( vips__ppm_header_stream( streamib, load->out ) ) { + VIPS_UNREF( streamib ); + return( -1 ); + } + + VIPS_UNREF( streamib ); + + return( 0 ); +} + +static int +vips_foreign_load_ppm_load( VipsForeignLoad *load ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; + + VipsStreami *streami; + VipsStreamib *streamib; + + if( !(streami = vips_streami_new_from_filename( ppm->filename )) ) + return( -1 ); + streamib = vips_streamib_new( streami ); + VIPS_UNREF( streami ); + + if( vips__ppm_load_stream( streamib, load->real ) ) { + VIPS_UNREF( streamib ); + return( -1 ); + } + + VIPS_UNREF( streamib ); + + return( 0 ); +} + +static void +vips_foreign_load_ppm_class_init( VipsForeignLoadPpmClass *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 = "ppmload"; + object_class->description = _( "load ppm from file" ); + + foreign_class->suffs = vips__ppm_suffs; + + /* We are fast at is_a(), so high priority. + */ + foreign_class->priority = 200; + + load_class->is_a = vips_foreign_load_ppm_is_a; + load_class->get_flags_filename = + vips_foreign_load_ppm_get_flags_filename; + load_class->get_flags = vips_foreign_load_ppm_get_flags; + load_class->header = vips_foreign_load_ppm_header; + load_class->load = vips_foreign_load_ppm_load; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPpm, filename ), + NULL ); +} + +static void +vips_foreign_load_ppm_init( VipsForeignLoadPpm *ppm ) +{ +} + +#endif /*HAVE_PPM*/ + +/** + * vips_ppmload: + * @filename: file to load + * @out: (out): output image + * @...: %NULL-terminated list of optional named arguments + * + * Read a PPM/PBM/PGM/PFM file into a VIPS image. + * + * It can read 1, 8, 16 and 32 bit images, colour or monochrome, + * stored in binary or in ASCII. One bit images become 8 bit VIPS images, + * with 0 and 255 for 0 and 1. + * + * See also: vips_image_new_from_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_ppmload( const char *filename, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "ppmload", ap, filename, out ); + va_end( ap ); + + return( result ); +} +