diff --git a/cplusplus/include/vips/vips-operators.h b/cplusplus/include/vips/vips-operators.h index 7466fcc7..92b30b1f 100644 --- a/cplusplus/include/vips/vips-operators.h +++ b/cplusplus/include/vips/vips-operators.h @@ -131,6 +131,7 @@ static VImage magickload_buffer( VipsBlob * buffer , VOption *options = 0 ); static VImage fitsload( char * filename , VOption *options = 0 ); static VImage openexrload( char * filename , VOption *options = 0 ); void radsave( char * filename , VOption *options = 0 ); +VipsBlob * radsave_buffer( VOption *options = 0 ); void ppmsave( char * filename , VOption *options = 0 ); void csvsave( char * filename , VOption *options = 0 ); void matrixsave( char * filename , VOption *options = 0 ); diff --git a/cplusplus/vips-operators.cpp b/cplusplus/vips-operators.cpp index 257234ad..ddbef979 100644 --- a/cplusplus/vips-operators.cpp +++ b/cplusplus/vips-operators.cpp @@ -1656,6 +1656,18 @@ void VImage::radsave( char * filename , VOption *options ) set( "filename", filename ) ); } +VipsBlob * VImage::radsave_buffer( VOption *options ) +{ + VipsBlob * buffer; + + call( "radsave_buffer" , + (options ? options : VImage::option()) -> + set( "in", *this ) -> + set( "buffer", &buffer ) ); + + return( buffer ); +} + void VImage::ppmsave( char * filename , VOption *options ) { call( "ppmsave" , diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 0f8f05d2..b0e754b6 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1618,7 +1618,8 @@ void vips_foreign_operation_init( void ) { extern GType vips_foreign_load_rad_get_type( void ); - extern GType vips_foreign_save_rad_get_type( void ); + extern GType vips_foreign_save_rad_file_get_type( void ); + extern GType vips_foreign_save_rad_buffer_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_save_ppm_get_type( void ); @@ -1688,7 +1689,8 @@ vips_foreign_operation_init( void ) #ifdef HAVE_RADIANCE vips_foreign_load_rad_get_type(); - vips_foreign_save_rad_get_type(); + vips_foreign_save_rad_file_get_type(); + vips_foreign_save_rad_buffer_get_type(); #endif /*HAVE_RADIANCE*/ #ifdef HAVE_POPPLER diff --git a/libvips/foreign/radiance.c b/libvips/foreign/radiance.c index fc688b60..e6d97da0 100644 --- a/libvips/foreign/radiance.c +++ b/libvips/foreign/radiance.c @@ -15,6 +15,8 @@ * 23/1/14 * - put the reader globals into a struct so we can have many active * readers + * 23/5/16 + * - Add buffer save functions */ /* @@ -132,12 +134,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include "radiance.h" @@ -810,29 +814,16 @@ scanline_read( Buffer *buffer, COLR *scanline, int width ) */ #define MAX_LINE (2 * MAXELEN * sizeof( COLR )) -/* Write a single scanline. +/* write an RLE scanline. Write magic header. */ -static int -scanline_write( COLR *scanline, int width, FILE *fp ) +static void +rle_scanline_write( COLR *scanline, int width, unsigned char *buffer, int *buffer_pos ) { - unsigned char buffer[MAX_LINE]; - int buffer_pos = 0; - #define PUTC( CH ) { \ - buffer[buffer_pos++] = (CH); \ - g_assert( buffer_pos <= MAX_LINE ); \ + buffer[(*buffer_pos)++] = (CH); \ + g_assert( *buffer_pos <= MAX_LINE ); \ } - int i, j, beg, cnt; - - if( width < MINELEN || - width > MAXELEN ) - /* Write as a flat scanline. - */ - return( fwrite( scanline, sizeof( COLR ), width, fp ) - width ); - - /* An RLE scanline. Write magic header. - */ PUTC( 2 ); PUTC( 2 ); PUTC( width >> 8 ); @@ -885,6 +876,25 @@ scanline_write( COLR *scanline, int width, FILE *fp ) } } } +} + +/* Write a single scanline. + */ +static int +scanline_write( COLR *scanline, int width, FILE *fp ) +{ + unsigned char buffer[MAX_LINE]; + int buffer_pos = 0; + + if( width < MINELEN || + width > MAXELEN ) + /* Write as a flat scanline. + */ + return( fwrite( scanline, sizeof( COLR ), width, fp ) - width ); + + /* An RLE scanline. + */ + rle_scanline_write( scanline, width, buffer, &buffer_pos ); return( fwrite( buffer, 1, buffer_pos, fp ) - buffer_pos ); } @@ -1151,7 +1161,7 @@ vips__rad_load( const char *filename, VipsImage *out, gboolean readbehind ) return( 0 ); } -/* What we track during a radiance file write. +/* What we track during a radiance write. */ typedef struct { VipsImage *in; @@ -1167,7 +1177,7 @@ typedef struct { } Write; static void -write_destroy( Write *write ) +write_destroy_file( Write *write ) { VIPS_FREE( write->filename ); VIPS_FREEF( fclose, write->fout ); @@ -1175,8 +1185,14 @@ write_destroy( Write *write ) vips_free( write ); } +static void +write_destroy( Write *write ) +{ + vips_free( write ); +} + static Write * -write_new( VipsImage *in, const char *filename ) +write_new( VipsImage *in) { Write *write; int i; @@ -1185,8 +1201,6 @@ write_new( VipsImage *in, const char *filename ) return( NULL ); write->in = in; - write->filename = vips_strdup( NULL, filename ); - write->fout = vips__file_open_write( filename, FALSE ); strcpy( write->format, COLRFMT ); write->expos = 1.0; for( i = 0; i < 3; i++ ) @@ -1201,16 +1215,11 @@ write_new( VipsImage *in, const char *filename ) write->prims[3][0] = CIE_x_w; write->prims[3][1] = CIE_y_w; - if( !write->filename || !write->fout ) { - write_destroy( write ); - return( NULL ); - } - return( write ); } -static int -vips2rad_put_header( Write *write ) +static void +vips2rad_make_header( Write *write ) { const char *str; int i, j; @@ -1240,6 +1249,12 @@ vips2rad_put_header( Write *write ) write->rs.rt = YDECR | YMAJOR; write->rs.xr = write->in->Xsize; write->rs.yr = write->in->Ysize; +} + +static int +vips2rad_put_header( Write *write ) +{ + vips2rad_make_header(write); fprintf( write->fout, "#?RADIANCE\n" ); @@ -1292,15 +1307,224 @@ vips__rad_save( VipsImage *in, const char *filename ) if( vips_image_pio_input( in ) || vips_check_coding_rad( "vips2rad", in ) ) return( -1 ); - if( !(write = write_new( in, filename )) ) + if( !(write = write_new( in )) ) return( -1 ); - if( vips2rad_put_header( write ) || + + write->filename = vips_strdup( NULL, filename ); + write->fout = vips__file_open_write( filename, FALSE ); + + if( !write->filename || !write->fout || + vips2rad_put_header( write ) || vips2rad_put_data( write ) ) { + write_destroy_file( write ); + return( -1 ); + } + write_destroy_file( write ); + + return( 0 ); +} + +typedef struct _WriteBuf { + char *buf; + size_t len; + size_t alloc; +} WriteBuf; + +static void +write_buf_free( WriteBuf *wbuf ) +{ + VIPS_FREE( wbuf->buf ); + VIPS_FREE( wbuf ); +} + +static WriteBuf * +write_buf_new( void ) +{ + WriteBuf *wbuf; + + if( !(wbuf = VIPS_NEW( NULL, WriteBuf )) ) + return( NULL ); + + wbuf->buf = NULL; + wbuf->len = 0; + wbuf->alloc = 0; + + return( wbuf ); +} + +static void +write_buf_grow( WriteBuf *wbuf, size_t grow_len ) +{ + size_t new_len = wbuf->len + grow_len; + + if( new_len > wbuf->alloc ) { + size_t proposed_alloc = (16 + wbuf->alloc) * 3 / 2; + + wbuf->alloc = VIPS_MAX( proposed_alloc, new_len ); + + /* There's no vips_realloc(), so we call g_realloc() directly. + * This is safe, since vips_malloc() / vips_free() are wrappers + * over g_malloc() / g_free(). + * + * FIXME: add vips_realloc(). + */ + wbuf->buf = g_realloc( wbuf->buf, wbuf->alloc ); + + VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n", + wbuf->alloc ); + } +} + +static void +bprintf( WriteBuf *wbuf, const char *fmt, ... ) +{ + int length = 0; + char *write_start = NULL; + va_list ap; + + /* Determine required size */ + va_start(ap, fmt); + length = vsnprintf(write_start, length, fmt, ap); + va_end(ap); + + write_buf_grow( wbuf, length + 1 ); + + write_start = wbuf->buf + wbuf->len; + + va_start(ap, fmt); + length = vsnprintf(write_start, length + 1, fmt, ap); + va_end(ap); + + wbuf->len += length; + + g_assert( wbuf->len <= wbuf->alloc ); +} + +#define bputformat(s,wb) bprintf(wb, "%s%s\n", FMTSTR, s) + +#define bputexpos(ex,wb) bprintf(wb,"%s%e\n",EXPOSSTR,ex) + +#define bputcolcor(cc,wb) bprintf(wb,"%s %f %f %f\n",COLCORSTR, \ + (cc)[RED],(cc)[GRN],(cc)[BLU]) + +#define bputaspect(pa,wb) bprintf(wb,"%s%f\n",ASPECTSTR,pa) + +#define bputprims(p,wb) bprintf(wb, \ + "%s %.4f %.4f %.4f %.4f %.4f %.4f %.4f %.4f\n",\ + PRIMARYSTR, \ + (p)[RED][CIEX],(p)[RED][CIEY], \ + (p)[GRN][CIEX],(p)[GRN][CIEY], \ + (p)[BLU][CIEX],(p)[BLU][CIEY], \ + (p)[WHT][CIEX],(p)[WHT][CIEY]) + +#define bputsresolu(rs,wb) bprintf(wb,"%s",resolu2str(resolu_buf,rs)) + +static int +vips2rad_put_header_buf( WriteBuf *wbuf, Write *write ) +{ + vips2rad_make_header(write); + + bprintf( wbuf, "#?RADIANCE\n" ); + + bputformat( write->format, wbuf ); + bputexpos( write->expos, wbuf ); + bputcolcor( write->colcor, wbuf ); + bprintf( wbuf, "SOFTWARE=vips %s\n", vips_version_string() ); + bputaspect( write->aspect, wbuf ); + bputprims( write->prims, wbuf ); + bprintf( wbuf, "\n" ); + bputsresolu( &write->rs, wbuf ); + + return( 0 ); +} + +/* Write a single scanline to buffer. + */ +static int +scanline_write_buf( COLR *scanline, int width, WriteBuf *wbuf ) +{ + int buffer_pos = 0; + + write_buf_grow( wbuf, MAX_LINE ); + unsigned char *buffer = (unsigned char *) wbuf->buf + wbuf->len; + + if( width < MINELEN || + width > MAXELEN ) { + /* Write as a flat scanline. + */ + memcpy( buffer, scanline, sizeof( COLR ) * width ); + wbuf->len += sizeof( COLR ) * width; + + return( 0 ); + } + + /* An RLE scanline. + */ + rle_scanline_write( scanline, width, buffer, &buffer_pos ); + + wbuf->len += buffer_pos; + return( 0 ); +} + +static int +vips2rad_put_data_block_buf( VipsRegion *region, VipsRect *area, void *a ) +{ + WriteBuf *wbuf = (WriteBuf *) a; + int i; + + for( i = 0; i < area->height; i++ ) { + VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + i ); + + if( scanline_write_buf( (COLR *) p, area->width, wbuf ) ) + return( -1 ); + } + + return( 0 ); +} + +static int +vips2rad_put_data_buf( WriteBuf *wbuf, Write *write ) +{ + if( vips_sink_disc( write->in, vips2rad_put_data_block_buf, wbuf ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen ) +{ + Write *write; + WriteBuf *wbuf; + +#ifdef DEBUG + printf( "vips2rad: writing to buffer\n" ); +#endif /*DEBUG*/ + + if( vips_image_pio_input( in ) || + vips_check_coding_rad( "vips2rad", in ) ) + return( -1 ); + if( !(wbuf = write_buf_new()) ) + return( -1 ); + if( !(write = write_new( in )) ) { + write_buf_free( wbuf ); + return( -1 ); + } + + if( vips2rad_put_header_buf( wbuf, write ) || + vips2rad_put_data_buf( wbuf, write ) ) { write_destroy( write ); + write_buf_free( wbuf ); return( -1 ); } write_destroy( write ); + *obuf = wbuf->buf; + wbuf->buf = NULL; + if( olen ) + *olen = wbuf->len; + + write_buf_free( wbuf ); return( 0 ); } diff --git a/libvips/foreign/radiance.h b/libvips/foreign/radiance.h index 95f5c3e4..71a259b8 100644 --- a/libvips/foreign/radiance.h +++ b/libvips/foreign/radiance.h @@ -40,6 +40,7 @@ int vips__rad_header( const char *filename, VipsImage *out ); int vips__rad_load( const char *filename, VipsImage *out, gboolean readbehind ); int vips__rad_save( VipsImage *in, const char *filename ); +int vips__rad_save_buf( VipsImage *in, void **obuf, size_t *olen ); extern const char *vips__rad_suffs[]; diff --git a/libvips/foreign/radsave.c b/libvips/foreign/radsave.c index bfffe6c1..7a4d0871 100644 --- a/libvips/foreign/radsave.c +++ b/libvips/foreign/radsave.c @@ -2,6 +2,8 @@ * * 2/12/11 * - wrap a class around the rad writer + * 23/5/16 + * - split into file and buffer save classes */ /* @@ -62,22 +64,6 @@ typedef VipsForeignSaveClass VipsForeignSaveRadClass; G_DEFINE_TYPE( VipsForeignSaveRad, vips_foreign_save_rad, VIPS_TYPE_FOREIGN_SAVE ); -static int -vips_foreign_save_rad_build( VipsObject *object ) -{ - VipsForeignSave *save = (VipsForeignSave *) object; - VipsForeignSaveRad *rad = (VipsForeignSaveRad *) object; - - if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_parent_class )-> - build( object ) ) - return( -1 ); - - if( vips__rad_save( save->ready, rad->filename ) ) - return( -1 ); - - return( 0 ); -} - /* Save a bit of typing. */ #define UC VIPS_FORMAT_UCHAR @@ -107,9 +93,8 @@ vips_foreign_save_rad_class_init( VipsForeignSaveRadClass *class ) gobject_class->set_property = vips_object_set_property; gobject_class->get_property = vips_object_get_property; - object_class->nickname = "radsave"; - object_class->description = _( "save image to Radiance file" ); - object_class->build = vips_foreign_save_rad_build; + object_class->nickname = "radsave_base"; + object_class->description = _( "save Radiance" ); foreign_class->suffs = vips__rad_suffs; @@ -118,12 +103,6 @@ vips_foreign_save_rad_class_init( VipsForeignSaveRadClass *class ) save_class->coding[VIPS_CODING_NONE] = FALSE; save_class->coding[VIPS_CODING_RAD] = TRUE; - VIPS_ARG_STRING( class, "filename", 1, - _( "Filename" ), - _( "Filename to save to" ), - VIPS_ARGUMENT_REQUIRED_INPUT, - G_STRUCT_OFFSET( VipsForeignSaveRad, filename ), - NULL ); } static void @@ -131,6 +110,120 @@ vips_foreign_save_rad_init( VipsForeignSaveRad *rad ) { } +typedef struct _VipsForeignSaveRadFile { + VipsForeignSaveRad parent_object; + + char *filename; +} VipsForeignSaveRadFile; + +typedef VipsForeignSaveRadClass VipsForeignSaveRadFileClass; + +G_DEFINE_TYPE( VipsForeignSaveRadFile, vips_foreign_save_rad_file, + vips_foreign_save_rad_get_type() ); + +static int +vips_foreign_save_rad_file_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSaveRadFile *rad_file = (VipsForeignSaveRadFile *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_file_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__rad_save( save->ready, rad_file->filename ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_save_rad_file_class_init( VipsForeignSaveRadFileClass *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 = "radsave"; + object_class->description = _( "save image to Radiance file" ); + object_class->build = vips_foreign_save_rad_file_build; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSaveRadFile, filename ), + NULL ); +} + +static void +vips_foreign_save_rad_file_init( VipsForeignSaveRadFile *file ) +{ +} + +typedef struct _VipsForeignSaveRadBuffer { + VipsForeignSaveRad parent_object; + + VipsArea *buf; +} VipsForeignSaveRadBuffer; + +typedef VipsForeignSaveRadClass VipsForeignSaveRadBufferClass; + +G_DEFINE_TYPE( VipsForeignSaveRadBuffer, vips_foreign_save_rad_buffer, + vips_foreign_save_rad_get_type() ); + +static int +vips_foreign_save_rad_buffer_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + + void *obuf; + size_t olen; + VipsBlob *blob; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_rad_buffer_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__rad_save_buf( save->ready, &obuf, &olen ) ) + return( -1 ); + + blob = vips_blob_new( (VipsCallbackFn) vips_free, obuf, olen ); + g_object_set( object, "buffer", blob, NULL ); + vips_area_unref( VIPS_AREA( blob ) ); + + return( 0 ); +} + +static void +vips_foreign_save_rad_buffer_class_init( VipsForeignSaveRadBufferClass *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 = "radsave_buffer"; + object_class->description = _( "save image to Radiance buffer" ); + object_class->build = vips_foreign_save_rad_buffer_build; + + VIPS_ARG_BOXED( class, "buffer", 1, + _( "Buffer" ), + _( "Buffer to save to" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsForeignSaveRadBuffer, buf ), + VIPS_TYPE_BLOB ); +} + +static void +vips_foreign_save_rad_buffer_init( VipsForeignSaveRadBuffer *buffer ) +{ +} + + #endif /*HAVE_RADIANCE*/ /** @@ -159,3 +252,49 @@ vips_radsave( VipsImage *in, const char *filename, ... ) return( result ); } + +/** + * vips_radsave_buffer: + * @in: image to save + * @buf: return output buffer here + * @len: return output length here + * @...: %NULL-terminated list of optional named arguments + * + * + * As vips_radsave(), but save to a memory buffer. + * + * The address of the buffer is returned in @obuf, the length of the buffer in + * @olen. You are responsible for freeing the buffer with g_free() when you + * are done with it. + * + * See also: vips_radsave(), vips_image_write_to_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) +{ + va_list ap; + VipsArea *area; + int result; + + area = NULL; + + va_start( ap, len ); + result = vips_call_split( "radsave_buffer", ap, in, &area ); + va_end( ap ); + + if( !result && + area ) { + if( buf ) { + *buf = area->data; + area->free_fn = NULL; + } + if( len ) + *len = area->length; + + vips_area_unref( area ); + } + + return( result ); +} \ No newline at end of file diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 218b7470..14d9a824 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -510,6 +510,8 @@ int vips_radload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); int vips_radsave( VipsImage *in, const char *filename, ... ) __attribute__((sentinel)); +int vips_radsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) + __attribute__((sentinel)); int vips_pdfload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel));