1 /* lazy open/save ... compat funcs for old im_open() behaviour
2  *
3  * 30/11/11
4  * 	- cut from old image.c
5  */
6 
7 /*
8 
9     This file is part of VIPS.
10 
11     VIPS is free software; you can redistribute it and/or modify
12     it under the terms of the GNU Lesser General Public License as published by
13     the Free Software Foundation; either version 2 of the License, or
14     (at your option) any later version.
15 
16     This program is distributed in the hope that it will be useful,
17     but WITHOUT ANY WARRANTY; without even the implied warranty of
18     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19     GNU Lesser General Public License for more details.
20 
21     You should have received a copy of the GNU Lesser General Public License
22     along with this program; if not, write to the Free Software
23     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24     02110-1301  USA
25 
26  */
27 
28 /*
29 
30     These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
31 
32  */
33 
34 /*
35 #define VIPS_DEBUG
36  */
37 
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif /*HAVE_CONFIG_H*/
41 #include <vips/intl.h>
42 
43 #include <errno.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <vips/vips.h>
49 #include <vips/vips7compat.h>
50 #include <vips/debug.h>
51 #include <vips/internal.h>
52 
53 static gboolean
vips_format_is_vips(VipsFormatClass * format)54 vips_format_is_vips( VipsFormatClass *format )
55 {
56 	return( strcmp( VIPS_OBJECT_CLASS( format )->nickname, "vips" ) == 0 );
57 }
58 
59 /* Lazy open.
60  */
61 
62 /* What we track during a delayed open.
63  */
64 typedef struct {
65 	VipsImage *image;
66 	VipsFormatClass *format;/* Read in pixels with this */
67 	char *filename;		/* Get pixels from here */
68 	gboolean sequential;	/* Sequential read requested */
69 
70 	VipsImage *real;	/* The real decompressed image */
71 } Lazy;
72 
73 static void
lazy_free_cb(VipsImage * image,Lazy * lazy)74 lazy_free_cb( VipsImage *image, Lazy *lazy )
75 {
76 	VIPS_DEBUG_MSG( "lazy_free: %p \"%s\"\n", lazy, lazy->filename );
77 
78 	g_free( lazy->filename );
79 	VIPS_UNREF( lazy->real );
80 	g_free( lazy );
81 }
82 
83 static Lazy *
lazy_new(VipsImage * image,VipsFormatClass * format,const char * filename,gboolean sequential)84 lazy_new( VipsImage *image,
85 	VipsFormatClass *format, const char *filename, gboolean sequential )
86 {
87 	Lazy *lazy;
88 
89 	lazy = g_new( Lazy, 1 );
90 	VIPS_DEBUG_MSG( "lazy_new: %p \"%s\"\n", lazy, filename );
91 	lazy->image = image;
92 	lazy->format = format;
93 	lazy->filename = g_strdup( filename );
94 	lazy->sequential = sequential;
95 	lazy->real = NULL;
96 	g_signal_connect( image, "close", G_CALLBACK( lazy_free_cb ), lazy );
97 
98 	return( lazy );
99 }
100 
101 static size_t
disc_threshold(void)102 disc_threshold( void )
103 {
104 	static gboolean done = FALSE;
105 	static size_t threshold;
106 
107 	if( !done ) {
108 		const char *env;
109 
110 		done = TRUE;
111 
112 		/* 100mb default.
113 		 */
114 		threshold = 100 * 1024 * 1024;
115 
116 		if( (env = g_getenv( "IM_DISC_THRESHOLD" )) )
117 			threshold = vips__parse_size( env );
118 
119 		if( vips__disc_threshold )
120 			threshold = vips__parse_size( vips__disc_threshold );
121 
122 		VIPS_DEBUG_MSG( "disc_threshold: %zd bytes\n", threshold );
123 	}
124 
125 	return( threshold );
126 }
127 
128 /* Make the real underlying image: either a direct disc file, or a temp file
129  * somewhere.
130  */
131 static VipsImage *
lazy_real_image(Lazy * lazy)132 lazy_real_image( Lazy *lazy )
133 {
134 	VipsImage *real;
135 
136 	/* We open via disc if:
137 	 * - 'sequential' is not set
138 	 * - disc_threshold() has not been set to zero
139 	 * - the format does not support lazy read
140 	 * - the uncompressed image will be larger than disc_threshold()
141 	 */
142 	real = NULL;
143 	if( !lazy->sequential &&
144 		disc_threshold() &&
145 	        !(vips_format_get_flags( lazy->format, lazy->filename ) &
146 			VIPS_FORMAT_PARTIAL) &&
147 		VIPS_IMAGE_SIZEOF_IMAGE( lazy->image ) > disc_threshold() )
148 			if( !(real = vips_image_new_temp_file( "%s.v" )) )
149 				return( NULL );
150 
151 	/* Otherwise, fall back to a "p".
152 	 */
153 	if( !real &&
154 		!(real = vips_image_new()) )
155 		return( NULL );
156 
157 	return( real );
158 }
159 
160 /* Our start function ... do the lazy open, if necessary, and return a region
161  * on the new image.
162  */
163 static void *
open_lazy_start(VipsImage * out,void * a,void * dummy)164 open_lazy_start( VipsImage *out, void *a, void *dummy )
165 {
166 	Lazy *lazy = (Lazy *) a;
167 
168 	if( !lazy->real ) {
169 		if( !(lazy->real = lazy_real_image( lazy )) ||
170 			lazy->format->load( lazy->filename, lazy->real ) ||
171 			vips_image_pio_input( lazy->real ) ) {
172 			VIPS_UNREF( lazy->real );
173 			return( NULL );
174 		}
175 	}
176 
177 	return( vips_region_new( lazy->real ) );
178 }
179 
180 /* Just copy.
181  */
182 static int
open_lazy_generate(VipsRegion * or,void * seq,void * a,void * b,gboolean * stop)183 open_lazy_generate( VipsRegion *or,
184 	void *seq, void *a, void *b, gboolean *stop )
185 {
186 	VipsRegion *ir = (VipsRegion *) seq;
187 
188         VipsRect *r = &or->valid;
189 
190         /* Ask for input we need.
191          */
192         if( vips_region_prepare( ir, r ) )
193                 return( -1 );
194 
195         /* Attach output region to that.
196          */
197         if( vips_region_region( or, ir, r, r->left, r->top ) )
198                 return( -1 );
199 
200         return( 0 );
201 }
202 
203 /* Lazy open ... init the header with the first OpenLazyFn, delay actually
204  * decoding pixels with the second OpenLazyFn until the first generate().
205  */
206 static int
vips_image_open_lazy(VipsImage * image,VipsFormatClass * format,const char * filename,gboolean sequential)207 vips_image_open_lazy( VipsImage *image,
208 	VipsFormatClass *format, const char *filename, gboolean sequential )
209 {
210 	Lazy *lazy;
211 
212 	lazy = lazy_new( image, format, filename, sequential );
213 
214 	/* Is there a ->header() function? We need to do a lazy load.
215 	 */
216 	if( format->header ) {
217 		/* Read header fields to init the return image.
218 		 */
219 		if( format->header( filename, image ) )
220 			return( -1 );
221 
222 		/* Then 'start' creates the real image and 'gen' paints 'image'
223 		 * with pixels from the real image on demand.
224 		 */
225 		if( vips_image_pipelinev( image, image->dhint, NULL ) ||
226 			vips_image_generate( image,
227 				open_lazy_start, open_lazy_generate,
228 				vips_stop_one, lazy, NULL ) )
229 			return( -1 );
230 	}
231 	else if( format->load ) {
232 		if( format->load( filename, image ) )
233 			return( -1 );
234 	}
235 	else
236 		g_assert( 0 );
237 
238 	return( 0 );
239 }
240 
241 /* Lazy save.
242  */
243 
244 /* If we write to (eg.) TIFF, actually do the write
245  * to a "p" and on "written" do im_vips2tiff() or whatever. Track save
246  * parameters here.
247  */
248 
249 typedef int (*VipsSaveFn)( VipsImage *image, const char *filename );
250 
251 typedef struct {
252 	VipsSaveFn save_fn;	/* Save function */
253 	char *filename;		/* Save args */
254 } SaveBlock;
255 
256 /* From "written" callback: invoke a delayed save.
257  */
258 static void
vips_image_save_cb(VipsImage * image,int * result,SaveBlock * sb)259 vips_image_save_cb( VipsImage *image, int *result, SaveBlock *sb )
260 {
261 	if( sb->save_fn( image, sb->filename ) )
262 		*result = -1;
263 
264 	g_free( sb->filename );
265 	g_free( sb );
266 }
267 
268 static void
vips_attach_save(VipsImage * image,VipsSaveFn save_fn,const char * filename)269 vips_attach_save( VipsImage *image, VipsSaveFn save_fn, const char *filename )
270 {
271 	SaveBlock *sb;
272 
273 	sb = g_new( SaveBlock, 1 );
274 	sb->save_fn = save_fn;
275 	sb->filename = g_strdup( filename );
276 	g_signal_connect( image, "written",
277 		G_CALLBACK( vips_image_save_cb ), sb );
278 }
279 
280 IMAGE *
vips__deprecated_open_read(const char * filename,gboolean sequential)281 vips__deprecated_open_read( const char *filename, gboolean sequential )
282 {
283 	VipsFormatClass *format;
284 
285 	if( !(format = vips_format_for_file( filename )) )
286 		return( NULL );
287 
288 	if( vips_format_is_vips( format ) ) {
289 		/* For vips format, we can just the main vips path.
290 		 */
291 		return( vips_image_new_mode( filename, "rd" ) );
292 	}
293 	else {
294 		/* For non-vips formats we must go via the old VipsFormat
295 		 * system to make sure we support the "filename:options"
296 		 * syntax.
297 		 */
298 		IMAGE *image;
299 
300 		image = vips_image_new();
301 		if( vips_image_open_lazy( image, format,
302 			filename, sequential ) ) {
303 			g_object_unref( image );
304 			return( NULL );
305 		}
306 
307 		/* Yuk. Can't g_object_set() filename since it's after
308 		 * construct. Just zap the new filename in.
309 		 */
310 		VIPS_SETSTR( image->filename, filename );
311 
312 		return( image );
313 	}
314 }
315 
316 IMAGE *
vips__deprecated_open_write(const char * filename)317 vips__deprecated_open_write( const char *filename )
318 {
319 	VipsFormatClass *format;
320 
321 	if( !(format = vips_format_for_name( filename )) )
322 		return( NULL );
323 
324 	if( vips_format_is_vips( format ) )
325 		/* For vips format, we can just the main vips path.
326 		 */
327 		return( vips_image_new_mode( filename, "w" ) );
328 	else {
329 		/* For non-vips formats we must go via the old VipsFormat
330 		 * system to make sure we support the "filename:options"
331 		 * syntax.
332 		 */
333 		IMAGE *image;
334 
335 		if( !(image = vips_image_new()) )
336 			return( NULL );
337 		vips_attach_save( image,
338 			format->save, filename );
339 		return( image );
340 	}
341 }
342