Exemplo n.º 1
0
int
im_png2vips( const char *name, IMAGE *out )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	gboolean sequential;
	VipsImage *x;

	im_filename_split( name, filename, mode );

	sequential = FALSE;
	p = &mode[0];
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			sequential = TRUE;
	}

	if( vips_pngload( filename, &x, 
		"sequential", sequential,
		NULL ) )
		return( -1 );

	if( vips_image_write( x, out ) ) {
		g_object_unref( x );
		return( -1 );
	}
	g_object_unref( x );

	return( 0 );
}
Exemplo n.º 2
0
static void *
vips_fits_write_meta( VipsImage *image, 
	const char *field, GValue *value, void *a )
{
	VipsFits *fits = (VipsFits *) a;

	int status;
	const char *value_str;

	status = 0;

	/* We want fields which start "fits-".
	 */
	if( !im_isprefix( "fits-", field ) )
		return( NULL );

	/* The value should be a refstring, since we wrote it in fits2vips 
	 * above ^^.
	 */
	value_str = im_ref_string_get( value );

	VIPS_DEBUG_MSG( "vips_fits_write_meta: setting meta on fits image:\n" );
	VIPS_DEBUG_MSG( " value == \"%s\"\n", value_str );

	if( fits_write_record( fits->fptr, value_str, &status ) ) {
		vips_fits_error( status );
		return( a );
	}

	return( NULL );
}
Exemplo n.º 3
0
/**
 * im_vips2csv:
 * @in: image to save 
 * @filename: file to write to 
 *
 * Save a CSV (comma-separated values) file. The image is written
 * one line of text per scanline. Complex numbers are written as 
 * "(real,imaginary)" and will need extra parsing I guess. The image must
 * have a single band.
 *
 * Write options can be embedded in the filename. The options can be given 
 * in any order and are:
 *
 * <itemizedlist>
 *   <listitem>
 *     <para>
 * <emphasis>sep:separator-string</emphasis> 
 * The string to use to separate numbers in the output. 
 * The default is "\\t" (tab).
 *     </para>
 *   </listitem>
 * </itemizedlist>
 *
 * For example:
 *
 * |[
 * im_csv2vips( in, "fred.csv:sep:\t" );
 * ]|
 *
 * Will write to fred.csv, separating numbers with tab characters.
 *
 * See also: #VipsFormat, im_csv2vips(), im_write_dmask(), im_vips2ppm().
 *
 * Returns: 0 on success, -1 on error.
 */
int 
im_vips2csv( IMAGE *in, const char *filename )
{
	char *separator = "\t";

	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	FILE *fp;
	char *p, *q, *r;

	/* Parse mode string.
	 */
	im_filename_split( filename, name, mode );
	p = &mode[0];
	while( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "sep", q ) && (r = im_getsuboption( q )) )
			separator = r;
	}

	if( im_incheck( in ) ||
		im_check_mono( "im_vips2csv", in ) ||
		im_check_uncoded( "im_vips2csv", in ) )
		return( -1 );

	if( !(fp = im__file_open_write( name, TRUE )) ) 
		return( -1 );
	if( vips2csv( in, fp, separator ) ) {
		fclose( fp );
		return( -1 );
	}

	fclose( fp );

	return( 0 );
}
Exemplo n.º 4
0
static int
tiff2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int page;
	int seq;

	im_filename_split( name, filename, mode );

	page = 0;
	seq = 0;
	p = &mode[0];
	if( (q = im_getnextoption( &p )) ) {
		page = atoi( q );
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			seq = 1;
	}

	/* We need to be compatible with the pre-sequential mode 
	 * im_tiff2vips(). This returned a "t" if given a "p" image, since it
	 * used writeline.
	 *
	 * If we're writing the image to a "p", switch it to a "t". And only
	 * for non-tiled (strip) images which we write with writeline.
	 *
	 * Don't do this for header read, since we don't want to force a
	 * malloc if all we are doing is looking at fields.
	 */

#ifdef HAVE_TIFF
	if( !header_only &&
		!seq &&
		!vips__istifftiled( filename ) &&
		out->dtype == VIPS_IMAGE_PARTIAL ) {
		if( vips__image_wio_output( out ) ) 
			return( -1 );
	}

	if( header_only ) {
		if( vips__tiff_read_header( filename, out, page ) )
			return( -1 );
	}
	else {
		if( vips__tiff_read( filename, out, page ) )
			return( -1 );
	}
#else
	vips_error( "im_tiff2vips", _( "no TIFF support in your libvips" ) ); 

	return( -1 );
#endif /*HAVE_TIFF*/

	return( 0 );
}
Exemplo n.º 5
0
int
im_jpeg2vips( const char *name, IMAGE *out )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	gboolean fail_on_warn;
	VipsImage *t;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}

	if( vips_jpegload( filename, &t, 
		"shrink", shrink,
		"fail", fail_on_warn,
		NULL ) )
		return( -1 );

	if( vips_image_write( t, out ) ) {
		g_object_unref( t );
		return( -1 );
	}
	g_object_unref( t );

	return( 0 );
}
Exemplo n.º 6
0
/* Process a single .desc line.
 */
static int
process_line( SymbolTable *st, const char *text )
{
	char line[1024];

#ifdef DEBUG
	printf( "read: %s\n", text );
#endif /*DEBUG*/

	/* We destroy line during the parse.
	 */
	im_strncpy( line, text, 1024 );

	if( im_isprefix( "#LRJOIN ", line ) || 
		im_isprefix( "#TBJOIN ", line ) ) {
		/* Yes: magic join command. Break into tokens. Format is eg.

			#LRJOIN <left> <right> <out> <x> <y> [<mwidth>]

		 */
		char *item[MAX_ITEMS];
		int nitems;
		JoinType type;
		JoinNode *arg1, *arg2, *join;
		int dx, dy, mwidth;

		if( (nitems = break_items( line, item )) < 0 )
			return( -1 );
		if( nitems != 5 && nitems != 6 ) {
			im_error( "global_balance", 
				_( "bad number of args in join line" ) );
			return( -1 );
		}

		if( !(arg1 = add_node( st, item[0] )) ||
			!(arg2 = add_node( st, item[1] )) ||
			!(join = add_node( st, item[2] )) )
			return( -1 );
		dx = atoi( item[3] );
		dy = atoi( item[4] );
		if( nitems == 6 ) 
			mwidth = atoi( item[5] );
		else
			mwidth = -1;
		if( im_isprefix( "#LRJOIN ", line ) )
			type = JOIN_LR;
		else
			type = JOIN_TB;

		if( make_join( st, type, arg1, arg2, 
			join, 1.0, 0.0, dx, dy, mwidth ) )
			return( -1 );
	}
	else if( im_isprefix( "#LRROTSCALE ", line ) ||
		im_isprefix( "#TBROTSCALE ", line ) ) {
		/* Rot + scale. Format is eg.

			#LRROTSCALE <left> <right> <out> \
				<a> <b> <x> <y> [<mwidth>]

		 */
		char *item[MAX_ITEMS];
		int nitems;
		JoinType type;
		JoinNode *arg1, *arg2, *join;
		double a, b, dx, dy;
		int mwidth;

		if( (nitems = break_items( line, item )) < 0 )
			return( -1 );
		if( nitems != 7 && nitems != 8 ) {
			im_error( "global_balance", 
				_( "bad number of args in join1 line" ) );
			return( -1 );
		}

		if( !(arg1 = add_node( st, item[0] )) ||
			!(arg2 = add_node( st, item[1] )) ||
			!(join = add_node( st, item[2] )) )
			return( -1 );
		a = g_ascii_strtod( item[3], NULL );
		b = g_ascii_strtod( item[4], NULL );
		dx = g_ascii_strtod( item[5], NULL );
		dy = g_ascii_strtod( item[6], NULL );
		if( nitems == 8 )
			mwidth = atoi( item[7] );
		else
			mwidth = -1;
		if( im_isprefix( "#LRROTSCALE ", line ) )
			type = JOIN_LRROTSCALE;
		else
			type = JOIN_TBROTSCALE;

		if( make_join( st, type, arg1, arg2, 
			join, a, b, dx, dy, mwidth ) )
			return( -1 );
	}
	else if( im_isprefix( "copy ", line ) ) {
		/* im_copy() call ... make a JOIN_CP node.
		 */
		char *item[MAX_ITEMS];
		int nitems;
		JoinNode *before, *after;

		if( (nitems = break_items( line, item )) < 0 )
			return( -1 );
		if( nitems != 2 ) {
			im_error( "global_balance", 
				_( "bad number of args in copy line" ) );
			return( -1 );
		}

		if( !(before = add_node( st, item[0] )) ||
			!(after = add_node( st, item[1] )) ||
			make_copy( st, before, after ) )
			return( -1 );
	}

	return( 0 );
}
Exemplo n.º 7
0
static int
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	int seq;
	gboolean fail_on_warn;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	seq = 0;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "seq", q ) )
			seq = 1;
	}

	/* Don't use vips_jpegload() ... we call the jpeg func directly in
	 * order to avoid the foreign.c mechanisms for load-via-disc and stuff
	 * like that.
	 */

	/* We need to be compatible with the pre-sequential mode 
	 * im_jpeg2vips(). This returned a "t" if given a "p" image, since it
	 * used writeline.
	 *
	 * If we're writing the image to a "p", switch it to a "t".
	 */

	if( !header_only &&
		!seq &&
		out->dtype == VIPS_IMAGE_PARTIAL ) {
		if( vips__image_wio_output( out ) ) 
			return( -1 );
	}

#ifdef HAVE_JPEG
	if( vips__jpeg_read_file( filename, out, 
		header_only, shrink, fail_on_warn, TRUE ) )
		return( -1 );
#else
	vips_error( "im_jpeg2vips", 
		"%s", _( "no JPEG support in your libvips" ) ); 

	return( -1 );
#endif /*HAVE_JPEG*/

	return( 0 );
}
Exemplo n.º 8
0
int
im_vips2dz( IMAGE *in, const char *filename )
{
	char *p, *q;
	char name[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char buf[FILENAME_MAX];

	int i;
	VipsForeignDzLayout layout = VIPS_FOREIGN_DZ_LAYOUT_DZ; 
	char *suffix = ".jpeg";
	int overlap = 0;
	int tile_size = 256;
	VipsForeignDzDepth depth = VIPS_FOREIGN_DZ_DEPTH_1PIXEL; 
	gboolean centre = FALSE;
	VipsAngle angle = VIPS_ANGLE_0; 

	/* We can't use im_filename_split() --- it assumes that we have a
	 * filename with an extension before the ':', and filename here is
	 * actually a dirname.
	 *
	 * Just split on the first ':'.
	 */
	im_strncpy( name, filename, FILENAME_MAX ); 
	if( (p = strchr( name, ':' )) ) {
		*p = '\0';
		im_strncpy( mode, p + 1, FILENAME_MAX ); 
	}

	strcpy( buf, mode ); 
	p = &buf[0];

	if( (q = im_getnextoption( &p )) ) {
		if( (i = vips_enum_from_nick( "im_vips2dz", 
			VIPS_TYPE_FOREIGN_DZ_LAYOUT, q )) < 0 ) 
			return( -1 );
		layout = i;
	}

	if( (q = im_getnextoption( &p )) ) 
		suffix = g_strdup( q );
	if( (q = im_getnextoption( &p )) ) 
		overlap = atoi( q ); 
	if( (q = im_getnextoption( &p )) ) 
		tile_size = atoi( q ); 

	if( (q = im_getnextoption( &p )) ) {
		if( (i = vips_enum_from_nick( "im_vips2dz", 
			VIPS_TYPE_FOREIGN_DZ_DEPTH, q )) < 0 )
			return( -1 );
		depth = i;
	}

	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "cen", q ) ) 
			centre = TRUE;
	}

	if( (q = im_getnextoption( &p )) ) {
		if( (i = vips_enum_from_nick( "im_vips2dz", 
			VIPS_TYPE_ANGLE, q )) < 0 )
			return( -1 );
		angle = i;
	}

	if( vips_dzsave( in, name,
		"layout", layout,
		"suffix", suffix,
		"overlap", overlap,
		"tile_size", tile_size,
		"depth", depth,
		"centre", centre,
		"angle", angle,
		NULL ) )
		return( -1 );

	return( 0 );
}
Exemplo n.º 9
0
/* Read a JPEG file into a VIPS image.
 */
static int
jpeg2vips( const char *name, IMAGE *out, gboolean header_only )
{
	char filename[FILENAME_MAX];
	char mode[FILENAME_MAX];
	char *p, *q;
	int shrink;
	struct jpeg_decompress_struct cinfo;
        ErrorManager eman;
	FILE *fp;
	int result;
	gboolean invert_pels;
	gboolean fail_on_warn;

	/* By default, we ignore any warnings. We want to get as much of
	 * the user's data as we can.
	 */
	fail_on_warn = FALSE;

	/* Parse the filename.
	 */
	im_filename_split( name, filename, mode );
	p = &mode[0];
	shrink = 1;
	if( (q = im_getnextoption( &p )) ) {
		shrink = atoi( q );

		if( shrink != 1 && shrink != 2 && 
			shrink != 4 && shrink != 8 ) {
			im_error( "im_jpeg2vips", 
				_( "bad shrink factor %d" ), shrink );
			return( -1 );
		}
	}
	if( (q = im_getnextoption( &p )) ) {
		if( im_isprefix( "fail", q ) ) 
			fail_on_warn = TRUE;
	}

	/* Make jpeg dcompression object.
 	 */
        cinfo.err = jpeg_std_error( &eman.pub );
	eman.pub.error_exit = new_error_exit;
	eman.pub.output_message = new_output_message;
	eman.fp = NULL;
	if( setjmp( eman.jmp ) ) {
		/* Here for longjmp() from new_error_exit().
		 */
		jpeg_destroy_decompress( &cinfo );

		return( -1 );
	}
        jpeg_create_decompress( &cinfo );

	/* Make input.
	 */
        if( !(fp = im__file_open_read( filename, NULL, FALSE )) ) 
                return( -1 );
	eman.fp = fp;
        jpeg_stdio_src( &cinfo, fp );

	/* Need to read in APP1 (EXIF metadata) and APP2 (ICC profile).
	 */
	jpeg_save_markers( &cinfo, JPEG_APP0 + 1, 0xffff );
	jpeg_save_markers( &cinfo, JPEG_APP0 + 2, 0xffff );

	/* Convert!
	 */
	result = read_jpeg_header( &cinfo, out, &invert_pels, shrink );
	if( !header_only && !result )
		result = read_jpeg_image( &cinfo, out, invert_pels );

	/* Close and tidy.
	 */
	fclose( fp );
	eman.fp = NULL;
	jpeg_destroy_decompress( &cinfo );

	if( eman.pub.num_warnings != 0 ) {
		if( fail_on_warn ) {
			im_error( "im_jpeg2vips", "%s", im_error_buffer() );
			result = -1;
		}
		else {
			im_warn( "im_jpeg2vips", _( "read gave %ld warnings" ), 
				eman.pub.num_warnings );
			im_warn( "im_jpeg2vips", "%s", im_error_buffer() );
		}
	}

	return( result );
}
Exemplo n.º 10
0
/* Read a cinfo to a VIPS image. Set invert_pels if the pixel reader needs to
 * do 255-pel.
 */
static int
read_jpeg_header( struct jpeg_decompress_struct *cinfo, 
	IMAGE *out, gboolean *invert_pels, int shrink )
{
	jpeg_saved_marker_ptr p;
	int type;

	/* Capture app2 sections here for assembly.
	 */
	void *app2_data[MAX_APP2_SECTIONS] = { 0 };
	int app2_data_length[MAX_APP2_SECTIONS] = { 0 };
	int data_length;
	int i;

	/* Read JPEG header. libjpeg will set out_color_space sanely for us 
	 * for YUV YCCK etc.
	 */
	jpeg_read_header( cinfo, TRUE );
	cinfo->scale_denom = shrink;
	cinfo->scale_num = 1;
	jpeg_calc_output_dimensions( cinfo );

	*invert_pels = FALSE;
	switch( cinfo->out_color_space ) {
	case JCS_GRAYSCALE:
		type = IM_TYPE_B_W;
		break;

	case JCS_CMYK:
		type = IM_TYPE_CMYK;
		/* Photoshop writes CMYK JPEG inverted :-( Maybe this is a
		 * way to spot photoshop CMYK JPGs.
		 */
		if( cinfo->saw_Adobe_marker ) 
			*invert_pels = TRUE;
		break;

	case JCS_RGB:
	default:
		type = IM_TYPE_sRGB;
		break;
	}

	/* Set VIPS header.
	 */
	im_initdesc( out,
		 cinfo->output_width, cinfo->output_height,
		 cinfo->output_components,
		 IM_BBITS_BYTE, IM_BANDFMT_UCHAR, IM_CODING_NONE, type,
		 1.0, 1.0, 0, 0 );

	/* Interlaced jpegs need lots of memory to read, so our caller needs
	 * to know.
	 */
	(void) im_meta_set_int( out, "jpeg-multiscan", 
		jpeg_has_multiple_scans( cinfo ) );

	/* Look for EXIF and ICC profile.
	 */
	for( p = cinfo->marker_list; p; p = p->next ) {
		switch( p->marker ) {
		case JPEG_APP0 + 1:
			/* EXIF data.
			 */
#ifdef DEBUG
			printf( "read_jpeg_header: seen %d bytes of APP1\n",
				p->data_length );
#endif /*DEBUG*/
			if( read_exif( out, p->data, p->data_length ) )
				return( -1 );
			break;

		case JPEG_APP0 + 2:
			/* ICC profile.
			 */
#ifdef DEBUG
			printf( "read_jpeg_header: seen %d bytes of APP2\n",
				p->data_length );
#endif /*DEBUG*/

			if( p->data_length > 14 &&
				im_isprefix( "ICC_PROFILE", 
					(char *) p->data ) ) {
				/* cur_marker numbers from 1, according to
				 * spec.
				 */
				int cur_marker = p->data[12] - 1;

				if( cur_marker >= 0 &&
					cur_marker < MAX_APP2_SECTIONS ) {
					app2_data[cur_marker] = p->data + 14;
					app2_data_length[cur_marker] = 
						p->data_length - 14;
				}
			}
			break;

		default:
#ifdef DEBUG
			printf( "read_jpeg_header: seen %d bytes of data\n",
				p->data_length );
#endif /*DEBUG*/
			break;
		}
	}

	/* Assemble ICC sections.
	 */
	data_length = 0;
	for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ )
		data_length += app2_data_length[i];
	if( data_length ) {
		unsigned char *data;
		int p;

#ifdef DEBUG
		printf( "read_jpeg_header: assembled %d byte ICC profile\n",
			data_length );
#endif /*DEBUG*/

		if( !(data = im_malloc( NULL, data_length )) ) 
			return( -1 );

		p = 0;
		for( i = 0; i < MAX_APP2_SECTIONS && app2_data[i]; i++ ) {
			memcpy( data + p, app2_data[i], app2_data_length[i] );
			p += app2_data_length[i];
		}

		if( im_meta_set_blob( out, IM_META_ICC_NAME, 
			(im_callback_fn) im_free, data, data_length ) ) {
			im_free( data );
			return( -1 );
		}
	}

	return( 0 );
}
Exemplo n.º 11
0
int
im_vips2tiff( IMAGE *in, const char *filename )
{
    char *p, *q, *r;
    char name[FILENAME_MAX];
    char mode[FILENAME_MAX];
    char buf[FILENAME_MAX];

    VipsForeignTiffCompression compression =
        VIPS_FOREIGN_TIFF_COMPRESSION_NONE;
    int Q = 75;
    VipsForeignTiffPredictor predictor = VIPS_FOREIGN_TIFF_PREDICTOR_NONE;
    char *profile = NULL;
    gboolean tile = FALSE;
    int tile_width = 128;
    int tile_height = 128;
    gboolean pyramid = FALSE;
    gboolean squash = FALSE;
    VipsForeignTiffResunit resunit = VIPS_FOREIGN_TIFF_RESUNIT_CM;
    double xres = in->Xres * 10.0;
    double yres = in->Yres * 10.0;
    gboolean bigtiff = FALSE;

    im_filename_split( filename, name, mode );
    strcpy( buf, mode );
    p = &buf[0];
    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "none", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_NONE;
        else if( im_isprefix( "packbits", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS;
        else if( im_isprefix( "ccittfax4", q ) )
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4;
        else if( im_isprefix( "lzw", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_LZW;

            if( (r = im_getsuboption( q )) ) {
                int i;

                if( sscanf( r, "%d", &i ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad predictor "
                                       "parameter" ) );
                    return( -1 );
                }
                predictor = i;
            }
        }
        else if( im_isprefix( "deflate", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE;

            if( (r = im_getsuboption( q )) ) {
                int i;

                if( sscanf( r, "%d", &i ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad predictor "
                                       "parameter" ) );
                    return( -1 );
                }
                predictor = i;
            }
        }
        else if( im_isprefix( "jpeg", q ) ) {
            compression = VIPS_FOREIGN_TIFF_COMPRESSION_JPEG;

            if( (r = im_getsuboption( q )) )
                if( sscanf( r, "%d", &Q ) != 1 ) {
                    im_error( "im_vips2tiff",
                              "%s", _( "bad JPEG quality "
                                       "parameter" ) );
                    return( -1 );
                }
        }
        else {
            im_error( "im_vips2tiff", _( "unknown compression mode "
                                         "\"%s\"\nshould be one of \"none\", "
                                         "\"packbits\", \"ccittfax4\", \"lzw\", "
                                         "\"deflate\" or \"jpeg\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "tile", q ) ) {
            tile = TRUE;

            if( (r = im_getsuboption( q )) ) {
                if( sscanf( r, "%dx%d",
                            &tile_width, &tile_height ) != 2 ) {
                    im_error( "im_vips2tiff", "%s",
                              _( "bad tile sizes" ) );
                    return( -1 );
                }
            }
        }
        else if( im_isprefix( "strip", q ) )
            tile = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown layout mode "
                                         "\"%s\"\nshould be one of \"tile\" or "
                                         "\"strip\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "pyramid", q ) )
            pyramid = TRUE;
        else if( im_isprefix( "flat", q ) )
            pyramid = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown multi-res mode "
                                         "\"%s\"\nshould be one of \"flat\" or "
                                         "\"pyramid\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "onebit", q ) )
            squash = TRUE;
        else if( im_isprefix( "manybit", q ) )
            squash = FALSE;
        else {
            im_error( "im_vips2tiff", _( "unknown format "
                                         "\"%s\"\nshould be one of \"onebit\" or "
                                         "\"manybit\"" ), q );
            return( -1 );
        }
    }

    if( (q = im_getnextoption( &p )) ) {
        if( im_isprefix( "res_cm", q ) )
            resunit = VIPS_FOREIGN_TIFF_RESUNIT_CM;
        else if( im_isprefix( "res_inch", q ) )
            resunit = VIPS_FOREIGN_TIFF_RESUNIT_INCH;
        else {
            im_error( "im_vips2tiff", _( "unknown resolution unit "
                                         "\"%s\"\nshould be one of \"res_cm\" or "
                                         "\"res_inch\"" ), q );
            return( -1 );
        }

        if( (r = im_getsuboption( q )) ) {
            if( sscanf( r, "%lfx%lf", &xres, &yres ) != 2 ) {
                if( sscanf( r, "%lf", &xres ) != 1 ) {
                    im_error( "im_vips2tiff", "%s",
                              _( "bad resolution values" ) );
                    return( -1 );
                }

                yres = xres;
            }

            /* vips resolutions are always in pixels/mm. If the
             * user specifies ",res_inch:72x72" then they are
             * using pixels/inch instead and we must convert.
             */
            if( resunit == VIPS_FOREIGN_TIFF_RESUNIT_INCH ) {
                xres /= 2.54;
                yres /= 2.54;
            }
        }
    }

    if( (q = im_getnextoption( &p )) && strcmp( q, "" ) != 0 )
        profile = im_strdup( NULL, q );

    if( (q = im_getnextoption( &p )) && strcmp( q, "8" ) == 0 )
        bigtiff = TRUE;

    if( (q = im_getnextoption( &p )) ) {
        im_error( "im_vips2tiff",
                  _( "unknown extra options \"%s\"" ), q );
        return( -1 );
    }

    if( vips_tiffsave( in, name,
                       "compression", compression,
                       "Q", Q,
                       "predictor", predictor,
                       "profile", profile,
                       "tile", tile,
                       "tile_width", tile_width,
                       "tile_height", tile_height,
                       "pyramid", pyramid,
                       "squash", squash,
                       "resunit", resunit,
                       "xres", xres,
                       "yres", yres,
                       "bigtiff", bigtiff,
                       NULL ) )
        return( -1 );

    return( 0 );
}