diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index c74d94b3..cbd2f8f4 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -3,6 +3,7 @@ noinst_LTLIBRARIES = libforeign.la libforeign_la_SOURCES = \ pforeign.h \ niftiload.c \ + niftisave.c \ quantise.c \ exif.c \ gifload.c \ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 74c935f2..20d6dc74 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1837,6 +1837,7 @@ vips_foreign_operation_init( void ) extern GType vips_foreign_load_svg_file_get_type( void ); extern GType vips_foreign_load_svg_buffer_get_type( void ); extern GType vips_foreign_load_nifti_get_type( void ); + extern GType vips_foreign_save_nifti_get_type( void ); extern GType vips_foreign_load_gif_get_type( void ); extern GType vips_foreign_load_gif_file_get_type( void ); extern GType vips_foreign_load_gif_buffer_get_type( void ); @@ -1961,6 +1962,7 @@ vips_foreign_operation_init( void ) #ifdef HAVE_NIFTI vips_foreign_load_nifti_get_type(); + vips_foreign_save_nifti_get_type(); #endif /*HAVE_NIFTI*/ vips__foreign_load_operation = diff --git a/libvips/foreign/niftiload.c b/libvips/foreign/niftiload.c index 4d9e544e..1f0692be 100644 --- a/libvips/foreign/niftiload.c +++ b/libvips/foreign/niftiload.c @@ -338,16 +338,6 @@ vips_foreign_load_nifti_set( const char *name, GValue *value, glong offset, return( NULL ); } -/* Slow and horrid version if there's no recent glib. - */ -#ifndef HAVE_CHECKED_MUL -#define g_uint_checked_mul( dest, a, b ) ( \ - ((guint64) a * b) > UINT_MAX ? \ - (*dest = UINT_MAX, FALSE) : \ - (*dest = a * b, TRUE) \ -) -#endif /*HAVE_CHECKED_MUL*/ - static int vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, nifti_image *nim, VipsImage *out ) @@ -406,7 +396,8 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, return( 0 ); } - if( !(fmt = vips__foreign_nifti_BandFmt2datatype( nim->datatype )) ) { + fmt = vips__foreign_nifti_datatype2BandFmt( nim->datatype ); + if( fmt == VIPS_FORMAT_NOTSET ) { vips_error( class->nickname, _( "datatype %d not supported" ), nim->datatype ); return( -1 ); @@ -459,7 +450,8 @@ vips_foreign_load_nifti_set_header( VipsForeignLoadNifti *nifti, /* Set some vips metadata for every nifti header field. */ - vips__foreign_nifti_map( vips_foreign_load_nifti_set, nim, out ); + if( vips__foreign_nifti_map( vips_foreign_load_nifti_set, nim, out ) ) + return( -1 ); /* One byte longer than the spec to leave space for any extra * '\0' termination. @@ -494,7 +486,7 @@ vips_foreign_load_nifti_header( VipsForeignLoad *load ) VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) load; /* We can't use the (much faster) nifti_read_header() since it just - * reads the 348 bytes iof the analyze struct and does not read any of + * reads the 348 bytes of the analyze struct and does not read any of * the extension fields. */ @@ -552,7 +544,8 @@ const char *vips__nifti_suffs[] = { ".hdr", ".hdr.gz", ".img", ".img.gz", ".nia", ".nia.gz", - NULL }; + NULL +}; static void vips_foreign_load_nifti_class_init( VipsForeignLoadNiftiClass *class ) diff --git a/libvips/foreign/niftisave.c b/libvips/foreign/niftisave.c index 0a2055f4..9b0531f2 100644 --- a/libvips/foreign/niftisave.c +++ b/libvips/foreign/niftisave.c @@ -47,7 +47,9 @@ #include -#ifdef HAVE_CFITSIO +#ifdef HAVE_NIFTI + +#include #include "pforeign.h" @@ -70,11 +72,11 @@ G_DEFINE_TYPE( VipsForeignSaveNifti, vips_foreign_save_nifti, static void vips_foreign_save_nifti_dispose( GObject *gobject ) { - VipsForeignLoadNifti *nifti = (VipsForeignLoadNifti *) gobject; + VipsForeignSaveNifti *nifti = (VipsForeignSaveNifti *) gobject; VIPS_FREEF( nifti_image_free, nifti->nim ); - G_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )-> + G_OBJECT_CLASS( vips_foreign_save_nifti_parent_class )-> dispose( gobject ); } @@ -91,24 +93,75 @@ vips_foreign_save_nifti_header_vips( VipsForeignSaveNifti *nifti, typedef struct _VipsNdimInfo { VipsImage *image; + nifti_image *nim; int *dims; int n; } VipsNdimInfo; static void * -vips_foreign_save_nifti_set_dims( const char *name, GValue *value, glong offset, - void *a, void *b ) +vips_foreign_save_nifti_set_dims( const char *name, + GValue *value, glong offset, void *a, void *b ) { VipsNdimInfo *info = (VipsNdimInfo *) a; /* The first 8 members are the dims fields. */ - if( info->n < 7 ) { - char txt[256]; + if( info->n < 8 ) { + char vips_name[256]; + int i; - vips_snprintf( txt, 256, "nifti-%s", name ); - if( vips_image_get_int( image, name, &info->dims[i] ) ) + vips_snprintf( vips_name, 256, "nifti-%s", name ); + if( vips_image_get_int( info->image, vips_name, &i ) || + i <= 0 || + i > VIPS_MAX_COORD ) return( info ); + info->dims[info->n] = i; + } + + info->n += 1; + + return( NULL ); +} + +/* How I wish glib had something like this :( Just implement the ones we need + * for vips_foreign_nifti_fields above. + */ +static void +vips_gvalue_write( GValue *value, void *p ) +{ + switch( G_VALUE_TYPE( value ) ) { + case G_TYPE_INT: + *((int *) p) = g_value_get_int( value ); + break; + + case G_TYPE_FLOAT: + *((float *) p) = g_value_get_float( value ); + break; + + default: + g_warning( "vips_gvalue_write: unsupported GType %s", + g_type_name( G_VALUE_TYPE( value ) ) ); + } +} + +static void * +vips_foreign_save_nifti_set_fields( const char *name, + GValue *value, glong offset, void *a, void *b ) +{ + VipsNdimInfo *info = (VipsNdimInfo *) a; + + /* The first 8 members are the dims fields. We set them above ^^^ -- + * do the others in this pass. + */ + if( info->n >= 8 ) { + char vips_name[256]; + GValue value_copy = { 0 }; + + vips_snprintf( vips_name, 256, "nifti-%s", name ); + if( vips_image_get( info->image, vips_name, &value_copy ) ) + return( info ); + vips_gvalue_write( &value_copy, (gpointer) info->nim + offset ); + g_value_unset( &value_copy ); } info->n += 1; @@ -127,7 +180,7 @@ vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti, VipsNdimInfo info; int dims[8]; int datatype; - int height; + guint height; int i; info.image = image; @@ -137,13 +190,18 @@ vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti, vips_foreign_save_nifti_set_dims, &info, NULL ) ) return( -1 ); - + /* FIXME what about page-height? should check that too. + */ height = 1; - for( i = 2; i < VIPS_NUMBER( dims ) && i < dims[0]; i++ ) - height *= dims[i]; - if( images->Xsize != dims[1] || - images->Ysize != height ) { + for( i = 2; i < VIPS_NUMBER( dims ) && i < dims[0] + 1; i++ ) + if( !g_uint_checked_mul( &height, height, dims[i] ) ) { + vips_error( class->nickname, + "%s", _( "dimension overflow" ) ); + return( 0 ); + } + if( image->Xsize != dims[1] || + image->Ysize != height ) { vips_error( class->nickname, "%s", _( "bad image dimensions" ) ); return( -1 ); @@ -156,9 +214,16 @@ vips_foreign_save_nifti_header_nifti( VipsForeignSaveNifti *nifti, return( -1 ); } - if( !(nnifti->nim = nifti_make_new_nim( dims, datatype, FALSE )) ) + if( !(nifti->nim = nifti_make_new_nim( dims, datatype, FALSE )) ) return( -1 ); + info.image = image; + info.nim = nifti->nim; + info.n = 0; + if( vips__foreign_nifti_map( + vips_foreign_save_nifti_set_fields, &info, NULL ) ) + return( -1 ); + return( 0 ); } @@ -167,8 +232,6 @@ vips_foreign_save_nifti_build( VipsObject *object ) { VipsForeignSave *save = (VipsForeignSave *) object; VipsForeignSaveNifti *nifti = (VipsForeignSaveNifti *) object; - VipsImage **t = (VipsImage **) - vips_object_local_array( VIPS_OBJECT( nifti ), 2 ); if( VIPS_OBJECT_CLASS( vips_foreign_save_nifti_parent_class )-> build( object ) ) @@ -192,7 +255,8 @@ vips_foreign_save_nifti_build( VipsObject *object ) /* set ext, plus other stuff */ - if( !(nim->data = vips_image_write_memory( save->ready, NULL )) ) + if( !(nifti->nim->data = + vips_image_write_to_memory( save->ready, NULL )) ) return( -1 ); /* No return code!??!?!! @@ -202,7 +266,7 @@ vips_foreign_save_nifti_build( VipsObject *object ) /* We must free and NULL the pointer or nifti will try to free it for * us. */ - VIPS_FREE( nim->data ); + VIPS_FREE( nifti->nim->data ); return( 0 ); } @@ -259,7 +323,7 @@ vips_foreign_save_nifti_init( VipsForeignSaveNifti *nifti ) { } -#endif /*HAVE_CFITSIO*/ +#endif /*HAVE_NIFTI*/ /** * vips_niftisave: (method) diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index a4864c19..bdffc683 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -35,6 +35,16 @@ extern "C" { #endif /*__cplusplus*/ +/* Slow and horrid version if there's no recent glib. + */ +#ifndef HAVE_CHECKED_MUL +#define g_uint_checked_mul( dest, a, b ) ( \ + ((guint64) a * b) > UINT_MAX ? \ + (*dest = UINT_MAX, FALSE) : \ + (*dest = a * b, TRUE) \ +) +#endif /*HAVE_CHECKED_MUL*/ + void vips__tiff_init( void ); int vips__tiff_write( VipsImage *in, const char *filename, @@ -256,6 +266,8 @@ int vips__quantise_image( VipsImage *in, VipsImage **index_out, VipsImage **palette_out, int colours, int Q, double dither ); +extern const char *vips__nifti_suffs[]; + VipsBandFormat vips__foreign_nifti_datatype2BandFmt( int datatype ); int vips__foreign_nifti_BandFmt2datatype( VipsBandFormat fmt );