From 70bde4a90d6aa2da4b3b4f389ccbff2a45111498 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 20 Dec 2011 09:37:07 +0000 Subject: [PATCH] move ppm read/write to new-style --- ChangeLog | 3 +- TODO | 8 +- libvips/foreign/Makefile.am | 4 + libvips/foreign/foreign.c | 4 + libvips/foreign/ppm.c | 731 +++++++++++++++++++++++++++++++++ libvips/foreign/ppm.h | 50 +++ libvips/foreign/ppmload.c | 166 ++++++++ libvips/foreign/ppmsave.c | 173 ++++++++ libvips/foreign/vipspng.h | 2 +- libvips/format/im_ppm2vips.c | 473 +-------------------- libvips/format/im_vips2ppm.c | 244 +---------- libvips/include/vips/foreign.h | 5 + 12 files changed, 1152 insertions(+), 711 deletions(-) create mode 100644 libvips/foreign/ppm.c create mode 100644 libvips/foreign/ppm.h create mode 100644 libvips/foreign/ppmload.c create mode 100644 libvips/foreign/ppmsave.c diff --git a/ChangeLog b/ChangeLog index f1446d00..177ddccf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,7 +15,8 @@ im_bandmean(), im_c2real(), im_c2imag(), im_ri2c(), im_jpeg*2vips(), im_vips2jpeg*(), im_tiff2vips(), im_vips2tiff(), im_exr2vips(), im_fits2vips(), im_vips2fits(), im_analyze2vips(), im_raw2vips(), - im_vips2raw(), im_magick2vips(), im_png2vips(), im_png2*() + im_vips2raw(), im_magick2vips(), im_png2vips(), im_png2*(), im_ppm2vips(), + im_vips2ppm() redone as classes - added argument priorites to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index aae2f040..6e279db4 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,6 @@ - vips2bufpng() is leaky -- interlace should be a bool? - - - -- radiance, matlab, ppm to go still +- radiance, matlab to go still - saverawfd should be renamed saveraw_fd, cf. pngsave_buffer @@ -14,6 +10,8 @@ do we need vips_image_wio_output()? can't vips_image_write_line() call this as well? + we need it in case we write to the data pointer of a "t" image I guess + - magickload is broken $ vips magickload healthygirl.jpg x.v diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index 9eba49d2..fe8765e1 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -1,6 +1,10 @@ noinst_LTLIBRARIES = libforeign.la libforeign_la_SOURCES = \ + ppm.h \ + ppm.c \ + ppmload.c \ + ppmsave.c \ csv.h \ csv.c \ csvload.c \ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 71d9bd75..7d4fc670 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1194,6 +1194,8 @@ vips_foreign_write_options( VipsImage *in, const char *filename ) void vips_foreign_operation_init( void ) { + extern GType vips_foreign_load_ppm_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_save_png_file_get_type( void ); extern GType vips_foreign_save_png_buffer_get_type( void ); @@ -1218,6 +1220,8 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_save_rawfd_get_type( void ); extern GType vips_foreign_load_magick_get_type( void ); + vips_foreign_load_ppm_get_type(); + vips_foreign_save_ppm_get_type(); vips_foreign_load_csv_get_type(); vips_foreign_save_csv_get_type(); vips_foreign_load_analyze_get_type(); diff --git a/libvips/foreign/ppm.c b/libvips/foreign/ppm.c new file mode 100644 index 00000000..5447ba92 --- /dev/null +++ b/libvips/foreign/ppm.c @@ -0,0 +1,731 @@ +/* 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 + */ + +/* + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include +#include +#include + +#include +#include + +/* The largest number/field/whatever we can read. + */ +#define MAX_THING (80) + +static void +skip_line( FILE *fp ) +{ + int ch; + + while( (ch = fgetc( fp )) != '\n' ) + ; +} + +static void +skip_white_space( FILE *fp ) +{ + int ch; + + while( isspace( ch = fgetc( fp ) ) ) + ; + ungetc( ch, fp ); + + if( ch == '#' ) { + skip_line( fp ); + skip_white_space( fp ); + } +} + +static int +read_int( FILE *fp, int *i ) +{ + skip_white_space( fp ); + if( fscanf( fp, "%d", i ) != 1 ) { + vips_error( "ppm2vips", "%s", _( "bad int" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +read_float( FILE *fp, float *f ) +{ + skip_white_space( fp ); + if( fscanf( fp, "%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( FILE *fp, 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] = fgetc( fp ); + buf[1] = fgetc( fp ); + 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( fp, &width ) || + read_int( fp, &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( fp, &scale ) ) + return( -1 ); + + /* Scale > 0 means big-endian. + */ + *msb_first = scale > 0; + vips_image_set_double( out, + "pfm-scale", fabs( scale ) ); + } + else { + int max_value; + + if( read_int( fp, &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( fgetc( fp ) ) ) { + 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( 0 ); + } + + 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 ); + + return( 0 ); +} + +/* Read a ppm/pgm file using mmap(). + */ +static int +read_mmap( FILE *fp, const char *filename, int msb_first, VipsImage *out ) +{ + const guint64 header_offset = ftell( fp ); + VipsImage *x = vips_image_new(); + VipsImage **t = (VipsImage **) + vips_object_local_array( VIPS_OBJECT( x ), 3 ); + + if( vips_rawload( filename, &t[0], + out->Xsize, out->Ysize, VIPS_IMAGE_SIZEOF_PEL( out ), + "offset", header_offset, + NULL ) || + vips_copy( t[0], &t[1], + "bands", out->Bands, + "format", out->BandFmt, + "coding", out->Coding, + NULL ) || + vips_copy( t[1], &t[2], + "swap", !vips_amiMSBfirst(), + NULL ) || + vips_image_write( t[2], out ) ) { + g_object_unref( x ); + return( -1 ); + } + g_object_unref( x ); + + return( 0 ); +} + +/* Read an ascii ppm/pgm file. + */ +static int +read_ascii( FILE *fp, VipsImage *out ) +{ + int x, y; + PEL *buf; + + if( vips_image_wio_output( out ) || + !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( read_int( fp, &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( 0 ); + } + } + + if( vips_image_write_line( out, y, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read an ascii 1 bit file. + */ +static int +read_1bit_ascii( FILE *fp, VipsImage *out ) +{ + int x, y; + PEL *buf; + + if( vips_image_wio_output( out ) || + !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + for( y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++ ) { + int val; + + if( read_int( fp, &val ) ) + return( -1 ); + + if( val == 1 ) + 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( FILE *fp, VipsImage *out ) +{ + int x, y, i; + int bits; + PEL *buf; + + if( vips_image_wio_output( out ) || + !(buf = VIPS_ARRAY( out, VIPS_IMAGE_SIZEOF_LINE( out ), PEL )) ) + return( -1 ); + + bits = fgetc( fp ); + for( i = 0, y = 0; y < out->Ysize; y++ ) { + for( x = 0; x < out->Xsize * out->Bands; x++, i++ ) { + buf[x] = (bits & 128) ? 255 : 0; + bits <<= 1; + if( (i & 7) == 7 ) + bits = fgetc( fp ); + } + + if( vips_image_write_line( out, y, buf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +parse_ppm( FILE *fp, const char *filename, VipsImage *out ) +{ + int bits; + int ascii; + int msb_first; + + if( read_header( fp, out, &bits, &ascii, &msb_first ) ) + return( -1 ); + + /* What sort of read are we doing? + */ + if( !ascii && bits >= 8 ) + return( read_mmap( fp, filename, msb_first, out ) ); + else if( !ascii && bits == 1 ) + return( read_1bit_binary( fp, out ) ); + else if( ascii && bits == 1 ) + return( read_1bit_ascii( fp, out ) ); + else + return( read_ascii( fp, out ) ); +} + +int +vips__ppm_header( const char *filename, VipsImage *out ) +{ + FILE *fp; + int bits; + int ascii; + int msb_first; + + if( !(fp = vips__file_open_read( filename, NULL, FALSE )) ) + return( -1 ); + if( read_header( fp, out, &bits, &ascii, &msb_first ) ) { + fclose( fp ); + return( -1 ); + } + + fclose( fp ); + + return( 0 ); +} + +/* Can this PPM file be read with a mmap? + */ +static int +isppmmmap( const char *filename ) +{ + VipsImage *im; + FILE *fp; + int bits; + int ascii; + int msb_first; + + if( !(fp = vips__file_open_read( filename, NULL, FALSE )) ) + return( -1 ); + + im = vips_image_new(); + if( read_header( fp, im, &bits, &ascii, &msb_first ) ) { + g_object_unref( im ); + fclose( fp ); + + return( 0 ); + } + g_object_unref( im ); + fclose( fp ); + + return( !ascii && bits >= 8 ); +} + +int +vips__ppm_load( const char *filename, VipsImage *out ) +{ + FILE *fp; + + /* Note that we open in binary mode. If this is a binary PPM, we need + * to be able to mmap it. + */ + if( !(fp = vips__file_open_read( filename, NULL, FALSE )) ) + return( -1 ); + if( parse_ppm( fp, filename, out ) ) { + fclose( fp ); + return( -1 ); + } + fclose( fp ); + + return( 0 ); +} + +int +vips__ppm_isppm( const char *filename ) +{ + PEL buf[3]; + + if( vips__get_bytes( filename, buf, 2 ) ) { + int i; + + buf[2] = '\0'; + for( i = 0; i < VIPS_NUMBER( magic_names ); i++ ) + if( strcmp( (char *) buf, magic_names[i] ) == 0 ) + return( TRUE ); + } + + return( 0 ); +} + +/* ppm flags function. + */ +VipsFormatFlags +vips__ppm_flags( const char *filename ) +{ + VipsFormatFlags flags; + + flags = 0; + if( isppmmmap( filename ) ) + flags |= VIPS_FORMAT_PARTIAL; + + return( flags ); +} + +const char *vips__ppm_suffs[] = { ".ppm", ".pgm", ".pbm", ".pfm", NULL }; + +typedef int (*write_fn)( VipsImage *in, FILE *fp, PEL *p ); + +/* What we track during a PPM write. + */ +typedef struct { + VipsImage *in; + FILE *fp; + char *name; + write_fn fn; +} Write; + +static void +write_destroy( Write *write ) +{ + VIPS_FREEF( fclose, write->fp ); + VIPS_FREE( write->name ); + + vips_free( write ); +} + +static Write * +write_new( VipsImage *in, const char *name ) +{ + Write *write; + + if( !(write = VIPS_NEW( NULL, Write )) ) + return( NULL ); + + write->in = in; + write->name = vips_strdup( NULL, name ); + write->fp = vips__file_open_write( name, FALSE ); + + if( !write->name || !write->fp ) { + write_destroy( write ); + return( NULL ); + } + + return( write ); +} + +static int +write_ppm_line_ascii( VipsImage *in, FILE *fp, PEL *p ) +{ + const int sk = VIPS_IMAGE_SIZEOF_PEL( in ); + int x, k; + + for( x = 0; x < in->Xsize; x++ ) { + for( k = 0; k < in->Bands; k++ ) { + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + fprintf( fp, "%d ", p[k] ); + break; + + case VIPS_FORMAT_USHORT: + fprintf( fp, "%d ", ((unsigned short *) p)[k] ); + break; + + case VIPS_FORMAT_UINT: + fprintf( fp, "%d ", ((unsigned int *) p)[k] ); + break; + + default: + g_assert( 0 ); + } + } + + fprintf( fp, " " ); + + p += sk; + } + + if( !fprintf( fp, "\n" ) ) { + vips_error( "vips2ppm", + "%s", _( "write error ... disc full?" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +write_ppm_line_binary( VipsImage *in, FILE *fp, PEL *p ) +{ + if( !fwrite( p, VIPS_IMAGE_SIZEOF_LINE( in ), 1, fp ) ) { + vips_error( "vips2ppm", + "%s", _( "write error ... disc full?" ) ); + return( -1 ); + } + + return( 0 ); +} + +static int +write_ppm_block( REGION *region, Rect *area, void *a ) +{ + Write *write = (Write *) a; + int i; + + for( i = 0; i < area->height; i++ ) { + PEL *p = (PEL *) VIPS_REGION_ADDR( region, 0, area->top + i ); + + if( write->fn( write->in, write->fp, p ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +write_ppm( Write *write, int ascii ) +{ + VipsImage *in = write->in; + + char *magic; + time_t timebuf; + + 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 ) + magic = "P2"; + 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( 0 ); + + fprintf( write->fp, "%s\n", magic ); + time( &timebuf ); + fprintf( write->fp, "#vips2ppm - %s\n", ctime( &timebuf ) ); + fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); + + switch( in->BandFmt ) { + case VIPS_FORMAT_UCHAR: + fprintf( write->fp, "%d\n", UCHAR_MAX ); + break; + + case VIPS_FORMAT_USHORT: + fprintf( write->fp, "%d\n", USHRT_MAX ); + break; + + case VIPS_FORMAT_UINT: + fprintf( write->fp, "%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; + fprintf( write->fp, "%g\n", scale ); +} + break; + + default: + g_assert( 0 ); + } + + 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( VipsImage *in, const char *filename, gboolean ascii ) +{ + 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 ); + + /* We can only write >8 bit binary images in float. + */ + if( vips_format_sizeof( in->BandFmt ) > 1 && + !ascii && + in->BandFmt != VIPS_FORMAT_FLOAT ) { + vips_error( "vips2ppm", + "%s", _( "binary >8 bit images must be float" ) ); + return( -1 ); + } + + if( !(write = write_new( in, filename )) ) + return( -1 ); + + if( write_ppm( write, ascii ) ) { + write_destroy( write ); + return( -1 ); + } + write_destroy( write ); + + return( 0 ); +} diff --git a/libvips/foreign/ppm.h b/libvips/foreign/ppm.h new file mode 100644 index 00000000..87c6975f --- /dev/null +++ b/libvips/foreign/ppm.h @@ -0,0 +1,50 @@ +/* common defs for ppm read/write + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef VIPS_PPM_H +#define VIPS_PPM_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +int vips__ppm_header( const char *name, VipsImage *out ); +int vips__ppm_load( const char *name, VipsImage *out ); +int vips__ppm_isppm( const char *filename ); +VipsFormatFlags vips__ppm_flags( const char *filename ); +extern const char *vips__ppm_suffs[]; + +int vips__ppm_save( VipsImage *in, const char *filename, + gboolean ascii ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_PPM_H*/ diff --git a/libvips/foreign/ppmload.c b/libvips/foreign/ppmload.c new file mode 100644 index 00000000..171cd405 --- /dev/null +++ b/libvips/foreign/ppmload.c @@ -0,0 +1,166 @@ +/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "ppm.h" + +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 VipsForeignFlags +vips_foreign_load_ppm_get_flags_filename( const char *filename ) +{ + return( vips__ppm_flags( filename ) ); +} + +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; + + if( vips__ppm_header( ppm->filename, load->out ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_ppm_load( VipsForeignLoad *load ) +{ + VipsForeignLoadPpm *ppm = (VipsForeignLoadPpm *) load; + + if( vips__ppm_load( ppm->filename, load->real ) ) + return( -1 ); + + 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; + + load_class->is_a = vips__ppm_isppm; + 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 ) +{ +} + +/** + * vips_ppmload: + * @filename: file to load + * @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 ); +} + diff --git a/libvips/foreign/ppmsave.c b/libvips/foreign/ppmsave.c new file mode 100644 index 00000000..a00be19f --- /dev/null +++ b/libvips/foreign/ppmsave.c @@ -0,0 +1,173 @@ +/* save to ppm + * + * 2/12/11 + * - wrap a class around the ppm 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 "ppm.h" + +typedef struct _VipsForeignSavePpm { + VipsForeignSave parent_object; + + char *filename; + gboolean ascii; +} VipsForeignSavePpm; + +typedef VipsForeignSaveClass VipsForeignSavePpmClass; + +G_DEFINE_TYPE( VipsForeignSavePpm, vips_foreign_save_ppm, + VIPS_TYPE_FOREIGN_SAVE ); + +static int +vips_foreign_save_ppm_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSavePpm *ppm = (VipsForeignSavePpm *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_ppm_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__ppm_save( save->ready, ppm->filename, ppm->ascii ) ) + 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_ppm[10] = { +/* UC C US S UI I F X D DX */ + UC, UC, US, US, UI, UI, F, F, F, F +}; + +static void +vips_foreign_save_ppm_class_init( VipsForeignSavePpmClass *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 = "ppmsave"; + object_class->description = _( "save image to ppm file" ); + object_class->build = vips_foreign_save_ppm_build; + + foreign_class->suffs = vips__ppm_suffs; + + 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" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePpm, ascii ), + FALSE ); +} + +static void +vips_foreign_save_ppm_init( VipsForeignSavePpm *ppm ) +{ +} + +/** + * vips_ppmsave: + * @in: image to save + * @filename: file to write to + * @ascii: save as ASCII rather than binary + * @...: %NULL-terminated list of optional named arguments + * + * Write a VIPS image to a file as PPM. It can write 8, 16 or + * 32 bit unsigned integer images, float images, colour or monochrome, + * stored as binary or ASCII. + * Integer images of more than 8 bits can only be stored in ASCII. + * + * When writing float (PFM) images the scale factor is set from the + * "pfm-scale" metadata. + * + * Set @ascii to %TRUE to write as human-readable ASCII. Normally data is + * written in binary. + * + * The storage format is indicated by a filename extension. Use one of .pbm, + * .pgm, .ppm, .pfm. + * + * See also: vips_image_write_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_ppmsave( VipsImage *in, const char *filename, ... ) +{ + va_list ap; + int result; + + va_start( ap, filename ); + result = vips_call_split( "ppmsave", ap, in, filename ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/foreign/vipspng.h b/libvips/foreign/vipspng.h index 1c6d687e..fea6b58d 100644 --- a/libvips/foreign/vipspng.h +++ b/libvips/foreign/vipspng.h @@ -1,4 +1,4 @@ -/* common defs for tiff read/write +/* common defs for png read/write */ /* diff --git a/libvips/format/im_ppm2vips.c b/libvips/format/im_ppm2vips.c index 7e607dc6..c9b36c97 100644 --- a/libvips/format/im_ppm2vips.c +++ b/libvips/format/im_ppm2vips.c @@ -1,28 +1,7 @@ /* 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 - * 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 - * 4/2/10 - * - gtkdoc - * 1/5/10 - * - add PFM (portable float map) support + * 20/12/11 + * - just a compat stub */ /* @@ -56,437 +35,20 @@ #endif /*HAVE_CONFIG_H*/ #include -#include -#include -#include -#include -#include - #include -#include -/* The largest number/field/whatever we can read. - */ -#define IM_MAX_THING (80) - -static void -skip_line( FILE *fp ) -{ - int ch; - - while( (ch = fgetc( fp )) != '\n' ) - ; -} - -static void -skip_white_space( FILE *fp ) -{ - int ch; - - while( isspace( ch = fgetc( fp ) ) ) - ; - ungetc( ch, fp ); - - if( ch == '#' ) { - skip_line( fp ); - skip_white_space( fp ); - } -} - -static int -read_int( FILE *fp, int *i ) -{ - skip_white_space( fp ); - if( fscanf( fp, "%d", i ) != 1 ) { - im_error( "im_ppm2vips", "%s", _( "bad int" ) ); - return( -1 ); - } - - return( 0 ); -} - -static int -read_float( FILE *fp, float *f ) -{ - skip_white_space( fp ); - if( fscanf( fp, "%f", f ) != 1 ) { - im_error( "im_ppm2vips", "%s", _( "bad float" ) ); - return( -1 ); - } - - return( 0 ); -} - -static int -read_header( FILE *fp, IMAGE *out, int *bits, int *ascii, int *msb_first ) -{ - int width, height, bands, fmt, type; - int index; - char buf[IM_MAX_THING]; - - /* 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 */ - }; - - /* 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] = fgetc( fp ); - buf[1] = fgetc( fp ); - buf[2] = '\0'; - - for( index = 0; index < IM_NUMBER( magic_names ); index++ ) - if( strcmp( magic_names[index], buf ) == 0 ) - break; - if( index == IM_NUMBER( magic_names ) ) { - im_error( "im_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( fp, &width ) || - read_int( fp, &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( fp, &scale ) ) - return( -1 ); - - /* Scale > 0 means big-endian. - */ - *msb_first = scale > 0; - im_meta_set_double( out, "pfm-scale", fabs( scale ) ); - } - else { - int max_value; - - if( read_int( fp, &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( fgetc( fp ) ) ) { - im_error( "im_ppm2vips", "%s", - _( "not whitespace before start of binary data" ) ); - return( -1 ); - } - - /* Choose a VIPS bandfmt. - */ - switch( *bits ) { - case 1: - case 8: - fmt = IM_BANDFMT_UCHAR; - break; - - case 16: - fmt = IM_BANDFMT_USHORT; - break; - - case 32: - if( index == 6 || index == 7 ) - fmt = IM_BANDFMT_FLOAT; - else - fmt = IM_BANDFMT_UINT; - break; - - default: - g_assert( 0 ); - } - - if( bands == 1 ) { - if( fmt == IM_BANDFMT_USHORT ) - type = IM_TYPE_GREY16; - else - type = IM_TYPE_B_W; - } - else { - if( fmt == IM_BANDFMT_USHORT ) - type = IM_TYPE_RGB16; - else if( fmt == IM_BANDFMT_UINT ) - type = IM_TYPE_RGB; - else - type = IM_TYPE_sRGB; - } - - im_initdesc( out, width, height, bands, (*bits == 1) ? 8 : *bits, fmt, - IM_CODING_NONE, - type, - 1.0, 1.0, - 0, 0 ); - - return( 0 ); -} - -/* Read a ppm/pgm file using mmap(). - */ -static int -read_mmap( FILE *fp, const char *filename, int msb_first, IMAGE *out ) -{ - const int header_offset = ftell( fp ); - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "im_ppm2vips", "p" ) || - im_raw2vips( filename, t[0], - out->Xsize, out->Ysize, - IM_IMAGE_SIZEOF_PEL( out ), header_offset ) || - im_copy_morph( t[0], t[1], - out->Bands, out->BandFmt, out->Coding ) || - im_copy_native( t[1], out, msb_first ) ) - return( -1 ); - - return( 0 ); -} - -/* Read an ascii ppm/pgm file. - */ -static int -read_ascii( FILE *fp, IMAGE *out ) -{ - int x, y; - PEL *buf; - - if( im_outcheck( out ) || im_setupout( out ) || - !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) - return( -1 ); - - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++ ) { - int val; - - if( read_int( fp, &val ) ) - return( -1 ); - - switch( out->BandFmt ) { - case IM_BANDFMT_UCHAR: - buf[x] = IM_CLIP( 0, val, 255 ); - break; - - case IM_BANDFMT_USHORT: - ((unsigned short *) buf)[x] = - IM_CLIP( 0, val, 65535 ); - break; - - case IM_BANDFMT_UINT: - ((unsigned int *) buf)[x] = val; - break; - - default: - g_assert( 0 ); - } - } - - if( im_writeline( y, out, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -/* Read an ascii 1 bit file. - */ -static int -read_1bit_ascii( FILE *fp, IMAGE *out ) -{ - int x, y; - PEL *buf; - - if( im_outcheck( out ) || im_setupout( out ) || - !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) - return( -1 ); - - for( y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++ ) { - int val; - - if( read_int( fp, &val ) ) - return( -1 ); - - if( val == 1 ) - buf[x] = 0; - else - buf[x] = 255; - } - - if( im_writeline( y, out, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -/* Read a 1 bit binary file. - */ -static int -read_1bit_binary( FILE *fp, IMAGE *out ) -{ - int x, y, i; - int bits; - PEL *buf; - - if( im_outcheck( out ) || im_setupout( out ) || - !(buf = IM_ARRAY( out, IM_IMAGE_SIZEOF_LINE( out ), PEL )) ) - return( -1 ); - - bits = fgetc( fp ); - for( i = 0, y = 0; y < out->Ysize; y++ ) { - for( x = 0; x < out->Xsize * out->Bands; x++, i++ ) { - buf[x] = (bits & 128) ? 255 : 0; - bits <<= 1; - if( (i & 7) == 7 ) - bits = fgetc( fp ); - } - - if( im_writeline( y, out, buf ) ) - return( -1 ); - } - - return( 0 ); -} - -static int -parse_ppm( FILE *fp, const char *filename, IMAGE *out ) -{ - int bits; - int ascii; - int msb_first; - - if( read_header( fp, out, &bits, &ascii, &msb_first ) ) - return( -1 ); - - /* What sort of read are we doing? - */ - if( !ascii && bits >= 8 ) - return( read_mmap( fp, filename, msb_first, out ) ); - else if( !ascii && bits == 1 ) - return( read_1bit_binary( fp, out ) ); - else if( ascii && bits == 1 ) - return( read_1bit_ascii( fp, out ) ); - else - return( read_ascii( fp, out ) ); -} - -static int -ppm2vips_header( const char *filename, IMAGE *out ) -{ - FILE *fp; - int bits; - int ascii; - int msb_first; - - if( !(fp = im__file_open_read( filename, NULL, FALSE )) ) - return( -1 ); - if( read_header( fp, out, &bits, &ascii, &msb_first ) ) { - fclose( fp ); - return( -1 ); - } - - fclose( fp ); - - return( 0 ); -} - -/* Can this PPM file be read with a mmap? - */ -static int -isppmmmap( const char *filename ) -{ - IMAGE *im; - FILE *fp; - int bits; - int ascii; - int msb_first; - - if( !(fp = im__file_open_read( filename, NULL, FALSE )) ) - return( -1 ); - - if( !(im = im_open( "temp", "p" )) ) { - fclose( fp ); - return( 0 ); - } - if( read_header( fp, im, &bits, &ascii, &msb_first ) ) { - im_close( im ); - fclose( fp ); - return( 0 ); - } - im_close( im ); - fclose( fp ); - - return( !ascii && bits >= 8 ); -} - -/** - * im_ppm2vips: - * @filename: file to load - * @out: image to write to - * - * 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. - * - * PFM images have the scale factor attached as "pfm-scale". - * - * See also: #VipsFormat, im_vips2ppm(), im_meta_get_double() - * - * Returns: 0 on success, -1 on error. - */ int im_ppm2vips( const char *filename, IMAGE *out ) { - FILE *fp; + VipsImage *t; - /* Note that we open in binary mode. If this is a binary PPM, we need - * to be able to mmap it. - */ - if( !(fp = im__file_open_read( filename, NULL, FALSE )) ) - return( -1 ); - if( parse_ppm( fp, filename, out ) ) { - fclose( fp ); + if( vips_ppmload( filename, &t, NULL ) ) + return( -1 ); + if( vips_image_write( t, out ) ) { + g_object_unref( t ); return( -1 ); } - fclose( fp ); + g_object_unref( t ); return( 0 ); } @@ -494,27 +56,13 @@ im_ppm2vips( const char *filename, IMAGE *out ) static int isppm( const char *filename ) { - unsigned char buf[2]; - - if( im__get_bytes( filename, buf, 2 ) ) - if( buf[0] == 'P' && (buf[1] >= '1' || buf[1] <= '6') ) - return( 1 ); - - return( 0 ); + return( vips_foreign_is_a( "ppmload", filename ) ); } -/* ppm flags function. - */ static VipsFormatFlags ppm_flags( const char *filename ) { - VipsFormatFlags flags; - - flags = 0; - if( isppmmmap( filename ) ) - flags |= VIPS_FORMAT_PARTIAL; - - return( flags ); + return( vips_foreign_flags( "ppmload", filename ) ); } static const char *ppm_suffs[] = { ".ppm", ".pgm", ".pbm", ".pfm", NULL }; @@ -534,7 +82,6 @@ vips_format_ppm_class_init( VipsFormatPpmClass *class ) object_class->description = _( "PPM/PBM/PNM/PFM" ); format_class->is_a = isppm; - format_class->header = ppm2vips_header; format_class->load = im_ppm2vips; format_class->save = im_vips2ppm; format_class->get_flags = ppm_flags; diff --git a/libvips/format/im_vips2ppm.c b/libvips/format/im_vips2ppm.c index 83997935..3fc21b9e 100644 --- a/libvips/format/im_vips2ppm.c +++ b/libvips/format/im_vips2ppm.c @@ -1,16 +1,7 @@ /* Write a ppm file. * - * 28/11/03 JC - * - better no-overshoot on tile loop - * 9/9/05 - * - tiny cleanups - * 3/11/07 - * - use im_wbuffer() for bg writes - * 4/2/10 - * - gtkdoc - * - cleanups - * 1/5/10 - * - add PFM (portable float map) support + * 20/12/11 + * - just a compat stub */ /* @@ -44,217 +35,13 @@ #endif /*HAVE_CONFIG_H*/ #include -#include -#include -#include #include -#include -#include #include -typedef int (*write_fn)( IMAGE *in, FILE *fp, PEL *p ); - -/* What we track during a PPM write. - */ -typedef struct { - IMAGE *in; - FILE *fp; - char *name; - write_fn fn; -} Write; - -static void -write_destroy( Write *write ) -{ - IM_FREEF( fclose, write->fp ); - IM_FREE( write->name ); - - im_free( write ); -} - -static Write * -write_new( IMAGE *in, const char *name ) -{ - Write *write; - - if( !(write = IM_NEW( NULL, Write )) ) - return( NULL ); - - write->in = in; - write->name = im_strdup( NULL, name ); - write->fp = im__file_open_write( name, FALSE ); - - if( !write->name || !write->fp ) { - write_destroy( write ); - return( NULL ); - } - - return( write ); -} - -static int -write_ppm_line_ascii( IMAGE *in, FILE *fp, PEL *p ) -{ - const int sk = IM_IMAGE_SIZEOF_PEL( in ); - int x, k; - - for( x = 0; x < in->Xsize; x++ ) { - for( k = 0; k < in->Bands; k++ ) { - switch( in->BandFmt ) { - case IM_BANDFMT_UCHAR: - fprintf( fp, "%d ", p[k] ); - break; - - case IM_BANDFMT_USHORT: - fprintf( fp, "%d ", ((unsigned short *) p)[k] ); - break; - - case IM_BANDFMT_UINT: - fprintf( fp, "%d ", ((unsigned int *) p)[k] ); - break; - - default: - g_assert( 0 ); - } - } - - fprintf( fp, " " ); - - p += sk; - } - - if( !fprintf( fp, "\n" ) ) { - im_error( "im_vips2ppm", - "%s", _( "write error ... disc full?" ) ); - return( -1 ); - } - - return( 0 ); -} - -static int -write_ppm_line_binary( IMAGE *in, FILE *fp, PEL *p ) -{ - if( !fwrite( p, IM_IMAGE_SIZEOF_LINE( in ), 1, fp ) ) { - im_error( "im_vips2ppm", - "%s", _( "write error ... disc full?" ) ); - return( -1 ); - } - - return( 0 ); -} - -static int -write_ppm_block( REGION *region, Rect *area, void *a ) -{ - Write *write = (Write *) a; - int i; - - for( i = 0; i < area->height; i++ ) { - PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + i ); - - if( write->fn( write->in, write->fp, p ) ) - return( -1 ); - } - - return( 0 ); -} - -static int -write_ppm( Write *write, int ascii ) -{ - IMAGE *in = write->in; - - char *magic; - time_t timebuf; - - if( in->BandFmt == IM_BANDFMT_FLOAT && in->Bands == 3 ) - magic = "PF"; - else if( in->BandFmt == IM_BANDFMT_FLOAT && in->Bands == 1 ) - magic = "Pf"; - else if( in->Bands == 1 && ascii ) - magic = "P2"; - 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( 0 ); - - fprintf( write->fp, "%s\n", magic ); - time( &timebuf ); - fprintf( write->fp, "#im_vips2ppm - %s\n", ctime( &timebuf ) ); - fprintf( write->fp, "%d %d\n", in->Xsize, in->Ysize ); - - switch( in->BandFmt ) { - case IM_BANDFMT_UCHAR: - fprintf( write->fp, "%d\n", UCHAR_MAX ); - break; - - case IM_BANDFMT_USHORT: - fprintf( write->fp, "%d\n", USHRT_MAX ); - break; - - case IM_BANDFMT_UINT: - fprintf( write->fp, "%d\n", UINT_MAX ); - break; - - case IM_BANDFMT_FLOAT: -{ - double scale; - - if( im_meta_get_double( in, "pfm-scale", &scale ) ) - scale = 1; - if( !im_amiMSBfirst() ) - scale *= -1; - fprintf( write->fp, "%g\n", scale ); -} - break; - - default: - g_assert( 0 ); - } - - 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 ); -} - -/** - * im_vips2ppm: - * @in: image to save - * @filename: file to write to - * - * Write a VIPS image to a file as PPM. It can write 8, 16 or - * 32 bit unsigned integer images, float images, colour or monochrome, - * stored as binary or ASCII. - * Integer images of more than 8 bits can only be stored in ASCII. - * - * When writing float (PFM) images the scale factor is set from the - * "pfm-scale" metadata. - * - * The storage format is indicated by a filename extension, for example: - * - * |[ - * im_vips2ppm( im, "fred.ppm:ascii" ) - * ]| - * - * will write to "fred.ppm" in ascii format. The default is binary. - * - * See also: #VipsFormat, im_ppm2vips(), im_meta_set_double(). - * - * Returns: 0 on success, -1 on error. - */ int im_vips2ppm( IMAGE *in, const char *filename ) { - Write *write; int ascii; char name[FILENAME_MAX]; char mode[FILENAME_MAX]; @@ -279,30 +66,5 @@ im_vips2ppm( IMAGE *in, const char *filename ) } } - if( im_check_uintorf( "im_vips2ppm", in ) || - im_check_bands_1or3( "im_vips2ppm", in ) || - im_check_uncoded( "im_vips2ppm", in ) || - im_pincheck( in ) ) - return( -1 ); - - /* We can only write >8 bit binary images in float. - */ - if( im_bits_of_fmt( in->BandFmt ) > 8 && - !ascii && - in->BandFmt != IM_BANDFMT_FLOAT ) { - im_error( "im_vips2ppm", - "%s", _( "binary >8 bit images must be float" ) ); - return( -1 ); - } - - if( !(write = write_new( in, name )) ) - return( -1 ); - - if( write_ppm( write, ascii ) ) { - write_destroy( write ); - return( -1 ); - } - write_destroy( write ); - - return( 0 ); + return( vips_ppmsave( in, filename, "ascii", ascii, NULL ) ); } diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index f198cd3c..98470c3d 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -356,6 +356,11 @@ int vips_pngsave( VipsImage *in, const char *filename, ... ) int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) __attribute__((sentinel)); +int vips_ppmload( const char *filename, VipsImage **out, ... ) + __attribute__((sentinel)); +int vips_ppmsave( VipsImage *in, const char *filename, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/