1 /* Manage pipelines of partial images.
2  *
3  * J.Cupitt, 17/4/93.
4  * 1/7/93 JC
5  *	- adapted for partial v2
6  *	- ANSIfied
7  * 6/7/93 JC
8  *	- im_setupout() conventions clarified - see autorewind in
9  *	  im_iocheck().
10  * 20/7/93 JC
11  *	- eval callbacks added
12  * 7/9/93 JC
13  *	- demand hint mechanism added
14  * 25/10/93
15  *	- asynchronous output mechanisms removed, as no observable speed-up
16  * 9/5/94
17  *      - new thread stuff added, with a define to turn it off
18  * 15/8/94
19  *	- start & stop functions can now be NULL for no-op
20  * 7/10/94 JC
21  *	- evalend callback system added
22  * 23/12/94 JC
23  *	- IM_ARRAY uses added
24  * 22/2/95 JC
25  *	- im_fill_copy() added
26  *	- im_region_region() uses modified
27  * 24/4/95 JC & KM
28  *	- im_fill_lines() bug removed
29  * 30/8/96 JC
30  *	- revised and simplified ... some code shared with im_iterate()
31  *	- new im_generate_region() added
32  * 2/3/98 JC
33  *	- IM_ANY added
34  * 20/7/99 JC
35  *	- tile geometry made into ints for easy tuning
36  * 30/7/99 RP JC
37  *	- threads reorganised for POSIX
38  * 29/9/99 JC
39  *	- threadgroup stuff added
40  * 15/4/04
41  *	- better how-many-pixels-calculated
42  * 27/11/06
43  * 	- merge background write stuff
44  * 7/11/07
45  * 	- new start/end eval callbacks
46  * 7/10/09
47  * 	- gtkdoc comments
48  * 16/4/10
49  * 	- remove threadgroup stuff
50  * 24/3/11
51  * 	- move demand_hint stuff in here
52  * 	- move to vips_ namespace
53  * 7/7/12
54  * 	- lock around link make/break so we can process an image from many
55  * 	  threads
56  */
57 
58 /*
59 
60     This file is part of VIPS.
61 
62     VIPS is free software; you can redistribute it and/or modify
63     it under the terms of the GNU Lesser General Public License as published by
64     the Free Software Foundation; either version 2 of the License, or
65     (at your option) any later version.
66 
67     This program is distributed in the hope that it will be useful,
68     but WITHOUT ANY WARRANTY; without even the implied warranty of
69     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
70     GNU Lesser General Public License for more details.
71 
72     You should have received a copy of the GNU Lesser General Public License
73     along with this program; if not, write to the Free Software
74     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
75     02110-1301  USA
76 
77  */
78 
79 /*
80 
81     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
82 
83  */
84 
85 /*
86 #define VIPS_DEBUG
87 #define DEBUG
88  */
89 
90 #ifdef HAVE_CONFIG_H
91 #include <config.h>
92 #endif /*HAVE_CONFIG_H*/
93 #include <vips/intl.h>
94 
95 #include <stdio.h>
96 #include <stdlib.h>
97 #include <stdarg.h>
98 #include <assert.h>
99 #include <errno.h>
100 #include <string.h>
101 #include <sys/types.h>
102 #ifdef HAVE_UNISTD_H
103 #include <unistd.h>
104 #endif /*HAVE_UNISTD_H*/
105 #ifdef HAVE_IO_H
106 #include <io.h>
107 #endif /*HAVE_IO_H*/
108 
109 #include <vips/vips.h>
110 #include <vips/internal.h>
111 #include <vips/thread.h>
112 #include <vips/debug.h>
113 
114 /**
115  * SECTION: generate
116  * @short_description: calculate pixels and pixel buffers
117  * @stability: Stable
118  * @see_also: <link linkend="VipsImage">VipsImage</link>,
119  * <link linkend="VipsRegion">VipsRegion</link>
120  * @include: vips/vips.h
121  *
122  * These functions let you attach generate functions to images
123  * and ask for regions of images to be calculated.
124  */
125 
126 /* Max number of images we can handle.
127  */
128 #define MAX_IMAGES (1000)
129 
130 /* Make an upstream/downstream link. upstream is one of downstream's inputs.
131  */
132 static void
vips__link_make(VipsImage * image_up,VipsImage * image_down)133 vips__link_make( VipsImage *image_up, VipsImage *image_down )
134 {
135 	g_assert( image_up );
136 	g_assert( image_down );
137 
138 	image_up->downstream =
139 		g_slist_prepend( image_up->downstream, image_down );
140 	image_down->upstream =
141 		g_slist_prepend( image_down->upstream, image_up );
142 
143 	/* Propogate the progress indicator.
144 	 */
145 	if( image_up->progress_signal &&
146 		!image_down->progress_signal )
147 		image_down->progress_signal = image_up->progress_signal;
148 }
149 
150 static void *
vips__link_break(VipsImage * image_up,VipsImage * image_down,void * b)151 vips__link_break( VipsImage *image_up, VipsImage *image_down, void *b )
152 {
153 	g_assert( image_up );
154 	g_assert( image_down );
155 
156 	g_assert( g_slist_find( image_up->downstream, image_down ) );
157 	g_assert( g_slist_find( image_down->upstream, image_up ) );
158 
159 	image_up->downstream =
160 		g_slist_remove( image_up->downstream, image_down );
161 	image_down->upstream =
162 		g_slist_remove( image_down->upstream, image_up );
163 
164 	/* Unlink the progress chain.
165 	 */
166 	if( image_down->progress_signal &&
167 		image_down->progress_signal == image_up->progress_signal )
168 		image_down->progress_signal = NULL;
169 
170 	return( NULL );
171 }
172 
173 static void *
vips__link_break_rev(VipsImage * image_down,VipsImage * image_up,void * b)174 vips__link_break_rev( VipsImage *image_down, VipsImage *image_up, void *b )
175 {
176 	return( vips__link_break( image_up, image_down, b ) );
177 }
178 
179 /* A VipsImage is going ... break all links.
180  */
181 void
vips__link_break_all(VipsImage * image)182 vips__link_break_all( VipsImage *image )
183 {
184 	g_mutex_lock( vips__global_lock );
185 
186 	vips_slist_map2( image->upstream,
187 		(VipsSListMap2Fn) vips__link_break, image, NULL );
188 	vips_slist_map2( image->downstream,
189 		(VipsSListMap2Fn) vips__link_break_rev, image, NULL );
190 
191 	g_assert( !image->upstream );
192 	g_assert( !image->downstream );
193 
194 	g_mutex_unlock( vips__global_lock );
195 }
196 
197 typedef struct _LinkMap {
198 	gboolean upstream;
199 	int serial;
200 	VipsSListMap2Fn fn;
201 	void *a;
202 	void *b;
203 } LinkMap;
204 
205 static void *
vips__link_mapp(VipsImage * image,LinkMap * map,void * b)206 vips__link_mapp( VipsImage *image, LinkMap *map, void *b )
207 {
208 	void *res;
209 
210 	/* Loop?
211 	 */
212 	if( image->serial == map->serial )
213 		return( NULL );
214 	image->serial = map->serial;
215 
216 	if( (res = map->fn( image, map->a, map->b )) )
217 		return( res );
218 
219 	return( vips_slist_map2( map->upstream ?
220 		image->upstream : image->downstream,
221 		(VipsSListMap2Fn) vips__link_mapp, map, NULL ) );
222 }
223 
224 static void *
vips__link_map_cb(VipsImage * image,GSList ** images,void * b)225 vips__link_map_cb( VipsImage *image, GSList **images, void *b )
226 {
227 	*images = g_slist_prepend( *images, image );
228 
229 	return( NULL );
230 }
231 
232 /* Apply a function to an image and all upstream or downstream images,
233  * direct and indirect.
234  */
235 void *
vips__link_map(VipsImage * image,gboolean upstream,VipsSListMap2Fn fn,void * a,void * b)236 vips__link_map( VipsImage *image, gboolean upstream,
237 	VipsSListMap2Fn fn, void *a, void *b )
238 {
239 	static int serial = 0;
240 
241 	LinkMap map;
242 	GSList *images;
243 	GSList *p;
244 	void *result;
245 
246 	images = NULL;
247 
248 	/* The function might do anything, including removing images
249 	 * or invalidating other images, so we can't trigger them from within
250 	 * the image loop. Instead we collect a list of images, ref them,
251 	 * run the functions, and unref.
252 	 */
253 
254 	map.upstream = upstream;
255 	map.fn = (VipsSListMap2Fn) vips__link_map_cb;
256 	map.a = (void *) &images;
257 	map.b = NULL;
258 
259 	/* We will be walking the tree of images and updating the ->serial
260 	 * member. There will be intense confusion if two threads try to do
261 	 * this at the same time.
262 	 */
263 	g_mutex_lock( vips__global_lock );
264 
265 	serial += 1;
266 	map.serial = serial;
267 
268 	vips__link_mapp( image, &map, NULL );
269 
270 	for( p = images; p; p = p->next )
271 		g_object_ref( p->data );
272 
273 	g_mutex_unlock( vips__global_lock );
274 
275 	result = vips_slist_map2( images, fn, a, b );
276 
277 	for( p = images; p; p = p->next )
278 		g_object_unref( p->data );
279 	g_slist_free( images );
280 
281 	return( result );
282 }
283 
284 /* We have to have this as a separate entry point so we can support the old
285  * vips7 API.
286  */
287 void
vips__demand_hint_array(VipsImage * image,VipsDemandStyle hint,VipsImage ** in)288 vips__demand_hint_array( VipsImage *image,
289 	VipsDemandStyle hint, VipsImage **in )
290 {
291 	int i, len, nany;
292 	VipsDemandStyle set_hint;
293 
294 	/* How many input images are there? And how many are ANY?
295 	 */
296 	for( i = 0, len = 0, nany = 0; in[i]; i++, len++ )
297 		if( in[i]->dhint == VIPS_DEMAND_STYLE_ANY )
298 			nany++;
299 
300 	/* Find the most restrictive of all the hints available to us.
301 	 *
302 	 * We have tried to be smarter about this in the past -- for example,
303 	 * detecting all ANY inputs and ignoring the hint in this case, but
304 	 * there are inevitably odd cases which cause problems. For example,
305 	 * new_from_memory, resize, affine, write_to_memory would run with
306 	 * FATSTRIP.
307 	 */
308 	set_hint = hint;
309 	for( i = 0; i < len; i++ )
310 		set_hint = (VipsDemandStyle) VIPS_MIN(
311 			(int) set_hint, (int) in[i]->dhint );
312 
313 	image->dhint = set_hint;
314 
315 #ifdef DEBUG
316         printf( "vips_image_pipeline_array: set dhint for \"%s\" to %s\n",
317 		image->filename,
318 		vips_enum_nick( VIPS_TYPE_DEMAND_STYLE, image->dhint ) );
319 	printf( "\toperation requested %s\n",
320 		vips_enum_nick( VIPS_TYPE_DEMAND_STYLE, hint ) );
321 	printf( "\tinputs were:\n" );
322 	printf( "\t" );
323 	for( i = 0; in[i]; i++ )
324 		printf( "%s ", vips_enum_nick( VIPS_TYPE_DEMAND_STYLE,
325 			in[i]->dhint ) );
326 	printf( "\n" );
327 #endif /*DEBUG*/
328 
329 	/* im depends on all these ims.
330 	 */
331 	g_mutex_lock( vips__global_lock );
332 	for( i = 0; i < len; i++ )
333 		vips__link_make( in[i], image );
334 	g_mutex_unlock( vips__global_lock );
335 
336 	/* Set a flag on the image to say we remembered to call this thing.
337 	 * vips_image_generate() and friends check this.
338 	 */
339 	image->hint_set = TRUE;
340 }
341 
342 /**
343  * vips_image_pipeline_array:
344  * @image: (out): output image
345  * @hint: demand hint for @image
346  * @in: (array zero-terminated=1): %NULL-terminated array of input images
347  *
348  * Add an image to a pipeline. @image depends on all of the images in @in,
349  * @image prefers to supply pixels according to @hint.
350  *
351  * Operations can set demand hints, that is, hints to the VIPS IO system about
352  * the type of region geometry they work best with. For example,
353  * operations which transform coordinates will usually work best with
354  * %VIPS_DEMAND_STYLE_SMALLTILE, operations which work on local windows of
355  * pixels will like %VIPS_DEMAND_STYLE_FATSTRIP.
356  *
357  * Header fields in @image are set from the fields in @in, with lower-numbered
358  * images in @in taking priority.
359  * For example, if @in[0] and @in[1] both have an item
360  * called "icc-profile", it's the profile attached to @in[0] that will end up
361  * on @image.
362  * Image history is completely copied from all @in. @image will have the history
363  * of all the input images.
364  * The array of input images can be empty, meaning @image is at the start of a
365  * pipeline.
366  *
367  * VIPS uses the list of input images to build the tree of operations it needs
368  * for the cache invalidation system.
369  *
370  * See also: vips_image_pipelinev(), vips_image_generate().
371  *
372  * Returns: 0 on success, -1 on error.
373  */
374 int
vips_image_pipeline_array(VipsImage * image,VipsDemandStyle hint,VipsImage ** in)375 vips_image_pipeline_array( VipsImage *image,
376 	VipsDemandStyle hint, VipsImage **in )
377 {
378 	/* This function can be called more than once per output image. For
379 	 * example, jpeg header load will call this once on ->out to set the
380 	 * default hint, then later call it again to connect the output image
381 	 * up to the real image.
382 	 *
383 	 * It's only ever called first time with in[0] == NULL and second time
384 	 * with a real value for @in.
385 	 */
386 	vips__demand_hint_array( image, hint, in );
387 
388 	if( in[0] &&
389 		vips__image_copy_fields_array( image, in ) )
390 		return( -1 );
391 
392 	if( vips__reorder_set_input( image, in ) )
393 		return( -1 );
394 
395 	return( 0 );
396 }
397 
398 /**
399  * vips_image_pipelinev:
400  * @image: output image of pipeline
401  * @hint: hint for this image
402  * @...: %NULL-terminated list of input images
403  *
404  * Build an array and call vips_image_pipeline_array().
405  *
406  * See also: vips_image_generate().
407  */
408 int
vips_image_pipelinev(VipsImage * image,VipsDemandStyle hint,...)409 vips_image_pipelinev( VipsImage *image, VipsDemandStyle hint, ... )
410 {
411 	va_list ap;
412 	int i;
413 	VipsImage *ar[MAX_IMAGES];
414 
415 	va_start( ap, hint );
416 	for( i = 0; i < MAX_IMAGES &&
417 		(ar[i] = va_arg( ap, VipsImage * )); i++ )
418 		;
419 	va_end( ap );
420 	if( i == MAX_IMAGES ) {
421 		g_warning( "%s", _( "too many images" ) );
422 
423 		/* Make sure we have a sentinel there.
424 		 */
425 		ar[i - 1] = NULL;
426 	}
427 
428 	return( vips_image_pipeline_array( image, hint, ar ) );
429 }
430 
431 /**
432  * vips_start_one:
433  * @out: image to generate
434  * @a: user data
435  * @b: user data
436  *
437  * Start function for one image in. Input image is @a.
438  *
439  * See also: vips_image_generate().
440  */
441 void *
vips_start_one(VipsImage * out,void * a,void * b)442 vips_start_one( VipsImage *out, void *a, void *b )
443 {
444 	VipsImage *in = (VipsImage *) a;
445 
446 	return( vips_region_new( in ) );
447 }
448 
449 /**
450  * vips_stop_one:
451  * @seq: sequence value
452  * @a: user data
453  * @b: user data
454  *
455  * Stop function for one image in. Input image is @a.
456  *
457  * See also: vips_image_generate().
458  */
459 int
vips_stop_one(void * seq,void * a,void * b)460 vips_stop_one( void *seq, void *a, void *b )
461 {
462 	VipsRegion *reg = (VipsRegion *) seq;
463 
464 	g_object_unref( reg );
465 
466 	return( 0 );
467 }
468 
469 /**
470  * vips_stop_many:
471  * @seq: sequence value
472  * @a: user data
473  * @b: user data
474  *
475  * Stop function for many images in. @a is a pointer to
476  * a %NULL-terminated array of input images.
477  *
478  * See also: vips_image_generate().
479  */
480 int
vips_stop_many(void * seq,void * a,void * b)481 vips_stop_many( void *seq, void *a, void *b )
482 {
483 	VipsRegion **ar = (VipsRegion **) seq;
484 
485         if( ar ) {
486 		int i;
487 
488 		for( i = 0; ar[i]; i++ )
489 			g_object_unref( ar[i] );
490 		g_free( (char *) ar );
491 	}
492 
493 	return( 0 );
494 }
495 
496 /**
497  * vips_start_many:
498  * @out: image to generate
499  * @a: user data
500  * @b: user data
501  *
502  * Start function for many images in. @a is a pointer to
503  * a %NULL-terminated array of input images.
504  *
505  * See also: vips_image_generate(), vips_allocate_input_array()
506  */
507 void *
vips_start_many(VipsImage * out,void * a,void * b)508 vips_start_many( VipsImage *out, void *a, void *b )
509 {
510 	VipsImage **in = (VipsImage **) a;
511 
512 	int i, n;
513 	VipsRegion **ar;
514 
515 	/* How many images?
516 	 */
517 	for( n = 0; in[n]; n++ )
518 		;
519 
520 	/* Alocate space for region array.
521 	 */
522 	if( !(ar = VIPS_ARRAY( NULL, n + 1, VipsRegion * )) )
523 		return( NULL );
524 
525 	/* Create a set of regions.
526 	 */
527 	for( i = 0; i < n; i++ )
528 		if( !(ar[i] = vips_region_new( in[i] )) ) {
529 			vips_stop_many( ar, NULL, NULL );
530 			return( NULL );
531 		}
532 	ar[n] = NULL;
533 
534 	return( ar );
535 }
536 
537 /**
538  * vips_allocate_input_array:
539  * @out: free array when this image closes
540  * @...: %NULL-terminated list of input images
541  *
542  * Convenience function --- make a %NULL-terminated array of input images.
543  * Use with vips_start_many().
544  *
545  * See also: vips_image_generate(), vips_start_many().
546  *
547  * Returns: %NULL-terminated array of images. Do not free the result.
548  */
549 VipsImage **
vips_allocate_input_array(VipsImage * out,...)550 vips_allocate_input_array( VipsImage *out, ... )
551 {
552 	va_list ap;
553 	VipsImage **ar;
554 	int i, n;
555 
556 	/* Count input images.
557 	 */
558 	va_start( ap, out );
559 	for( n = 0; va_arg( ap, VipsImage * ); n++ )
560 		;
561 	va_end( ap );
562 
563 	/* Allocate array.
564 	 */
565 	if( !(ar = VIPS_ARRAY( out, n + 1, VipsImage * )) )
566 		return( NULL );
567 
568 	/* Fill array.
569 	 */
570 	va_start( ap, out );
571 	for( i = 0; i < n; i++ )
572 		ar[i] = va_arg( ap, VipsImage * );
573 	va_end( ap );
574 	ar[n] = NULL;
575 
576 	return( ar );
577 }
578 
579 /**
580  * VipsStartFn:
581  * @out: image being calculated
582  * @a: user data
583  * @b: user data
584  *
585  * Start a new processing sequence for this generate function. This allocates
586  * per-thread state, such as an input region.
587  *
588  * See also: vips_start_one(), vips_start_many().
589  *
590  * Returns: a new sequence value
591  */
592 
593 /**
594  * VipsGenerateFn:
595  * @out: #VipsRegion to fill
596  * @seq: sequence value
597  * @a: user data
598  * @b: user data
599  * @stop: set this to stop processing
600  *
601  * Fill @out->valid with pixels. @seq contains per-thread state, such as the
602  * input regions. Set @stop to %TRUE to stop processing.
603  *
604  * See also: vips_image_generate(), vips_stop_many().
605  *
606  * Returns: 0 on success, -1 on error.
607  */
608 
609 /**
610  * VipsStopFn:
611  * @seq: sequence value
612  * @a: user data
613  * @b: user data
614  *
615  * Stop a processing sequence. This frees
616  * per-thread state, such as an input region.
617  *
618  * See also: vips_stop_one(), vips_stop_many().
619  *
620  * Returns: 0 on success, -1 on error.
621  */
622 
623 /* A write function for VIPS images. Just write() the pixel data.
624  */
625 static int
write_vips(VipsRegion * region,VipsRect * area,void * a)626 write_vips( VipsRegion *region, VipsRect *area, void *a )
627 {
628 	size_t nwritten, count;
629 	void *buf;
630 
631 	count = (size_t) region->bpl * area->height;
632 	buf = VIPS_REGION_ADDR( region, 0, area->top );
633 
634 	do {
635 		nwritten = write( region->im->fd, buf, count );
636 		if( nwritten == (size_t) -1 )
637 			return( errno );
638 
639 		buf = (void *) ((char *) buf + nwritten);
640 		count -= nwritten;
641 	} while( count > 0 );
642 
643 	return( 0 );
644 }
645 
646 /**
647  * vips_image_generate:
648  * @image: generate this image
649  * @start_fn: start sequences with this function
650  * @generate_fn: generate pixels with this function
651  * @stop_fn: stop sequences with this function
652  * @a: user data
653  * @b: user data
654  *
655  * Generates an image. The action depends on the image type.
656  *
657  * For images created with vips_image_new(), vips_image_generate() just
658  * attaches the start/generate/stop callbacks and returns.
659  *
660  * For images created with vips_image_new_memory(), memory is allocated for
661  * the whole image and it is entirely generated using vips_sink_memory().
662  *
663  * For images created with vips_image_new_temp_file() and friends, memory for
664  * a few scanlines is allocated and
665  * vips_sink_disc() used to generate the image in small chunks. As each
666  * chunk is generated, it is written to disc.
667  *
668  * See also: vips_sink(), vips_image_new(), vips_region_prepare().
669  *
670  * Returns: 0 on success, or -1 on error.
671  */
672 int
vips_image_generate(VipsImage * image,VipsStartFn start_fn,VipsGenerateFn generate_fn,VipsStopFn stop_fn,void * a,void * b)673 vips_image_generate( VipsImage *image,
674 	VipsStartFn start_fn, VipsGenerateFn generate_fn, VipsStopFn stop_fn,
675         void *a, void *b )
676 {
677         int res;
678 
679 	VIPS_DEBUG_MSG( "vips_image_generate: %p\n", image );
680 
681 	g_assert( generate_fn );
682 	g_assert( vips_object_sanity( VIPS_OBJECT( image ) ) );
683 
684 	if( !image->hint_set ) {
685 		vips_error( "vips_image_generate",
686 			"%s", _( "demand hint not set" ) );
687 		return( -1 );
688 	}
689 
690 	/* We don't use this, but make sure it's set in case any old binaries
691 	 * are expecting it.
692 	 */
693 	image->Bbits = vips_format_sizeof( image->BandFmt ) << 3;
694 
695         /* Look at output type to decide our action.
696          */
697         switch( image->dtype ) {
698         case VIPS_IMAGE_PARTIAL:
699                 /* Output to partial image. Just attach functions and return.
700                  */
701                 if( image->generate_fn ||
702 			image->start_fn ||
703 			image->stop_fn ) {
704                         vips_error( "VipsImage",
705 				"%s", _( "generate() called twice" ) );
706                         return( -1 );
707                 }
708 
709                 image->start_fn = start_fn;
710                 image->generate_fn = generate_fn;
711                 image->stop_fn = stop_fn;
712                 image->client1 = a;
713                 image->client2 = b;
714 
715                 VIPS_DEBUG_MSG( "vips_image_generate: "
716 			"attaching partial callbacks\n" );
717 
718 		if( vips_image_written( image ) )
719 			return( -1 );
720 
721                 break;
722 
723         case VIPS_IMAGE_SETBUF:
724         case VIPS_IMAGE_SETBUF_FOREIGN:
725         case VIPS_IMAGE_MMAPINRW:
726         case VIPS_IMAGE_OPENOUT:
727                 /* Eval now .. sanity check.
728                  */
729                 if( image->generate_fn ||
730 			image->start_fn ||
731 			image->stop_fn ) {
732                         vips_error( "VipsImage",
733 				"%s", _( "generate() called twice" ) );
734                         return( -1 );
735                 }
736 
737                 /* Attach callbacks.
738                  */
739                 image->start_fn = start_fn;
740                 image->generate_fn = generate_fn;
741                 image->stop_fn = stop_fn;
742                 image->client1 = a;
743                 image->client2 = b;
744 
745                 if( vips_image_write_prepare( image ) )
746                         return( -1 );
747 
748                 if( image->dtype == VIPS_IMAGE_OPENOUT )
749 			res = vips_sink_disc( image, write_vips, NULL );
750                 else
751                         res = vips_sink_memory( image );
752 
753                 /* Error?
754                  */
755                 if( res )
756                         return( -1 );
757 
758 		/* Must come before we rewind.
759 		 */
760 		if( vips_image_written( image ) )
761 			return( -1 );
762 
763 		/* We've written to image ... rewind it ready for reading.
764 		 */
765 		if( vips_image_pio_input( image ) )
766 			return( -1 );
767 
768                 break;
769 
770         default:
771                 /* Not a known output style.
772                  */
773 		vips_error( "VipsImage",
774 			_( "unable to output to a %s image" ),
775 			vips_enum_nick( VIPS_TYPE_IMAGE_TYPE,
776 				image->dtype ) );
777                 return( -1 );
778         }
779 
780         return( 0 );
781 }
782