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