1 : /******************************************************************************
2 : * $Id: vsidataio.cpp 15508 2008-10-09 21:24:31Z warmerdam $
3 : *
4 : * Project: JPEG JFIF Driver
5 : * Purpose: Implement JPEG read/write io indirection through VSI.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : * Code partially derived from libjpeg jdatasrc.c and jdatadst.c.
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : ****************************************************************************/
30 :
31 : #include "cpl_vsi.h"
32 :
33 : CPL_CVSID("$Id: vsidataio.cpp 15508 2008-10-09 21:24:31Z warmerdam $");
34 :
35 : CPL_C_START
36 : #include "jpeglib.h"
37 : #include "jerror.h"
38 : CPL_C_END
39 :
40 :
41 : /* Expanded data source object for stdio input */
42 :
43 : typedef struct {
44 : struct jpeg_source_mgr pub; /* public fields */
45 :
46 : FILE * infile; /* source stream */
47 : JOCTET * buffer; /* start of buffer */
48 : boolean start_of_file; /* have we gotten any data yet? */
49 : } my_source_mgr;
50 :
51 : typedef my_source_mgr * my_src_ptr;
52 :
53 : #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */
54 :
55 :
56 : /*
57 : * Initialize source --- called by jpeg_read_header
58 : * before any data is actually read.
59 : */
60 :
61 : METHODDEF(void)
62 120 : init_source (j_decompress_ptr cinfo)
63 : {
64 120 : my_src_ptr src = (my_src_ptr) cinfo->src;
65 :
66 : /* We reset the empty-input-file flag for each image,
67 : * but we don't clear the input buffer.
68 : * This is correct behavior for reading a series of images from one source.
69 : */
70 120 : src->start_of_file = TRUE;
71 120 : }
72 :
73 :
74 : /*
75 : * Fill the input buffer --- called whenever buffer is emptied.
76 : *
77 : * In typical applications, this should read fresh data into the buffer
78 : * (ignoring the current state of next_input_byte & bytes_in_buffer),
79 : * reset the pointer & count to the start of the buffer, and return TRUE
80 : * indicating that the buffer has been reloaded. It is not necessary to
81 : * fill the buffer entirely, only to obtain at least one more byte.
82 : *
83 : * There is no such thing as an EOF return. If the end of the file has been
84 : * reached, the routine has a choice of ERREXIT() or inserting fake data into
85 : * the buffer. In most cases, generating a warning message and inserting a
86 : * fake EOI marker is the best course of action --- this will allow the
87 : * decompressor to output however much of the image is there. However,
88 : * the resulting error message is misleading if the real problem is an empty
89 : * input file, so we handle that case specially.
90 : *
91 : * In applications that need to be able to suspend compression due to input
92 : * not being available yet, a FALSE return indicates that no more data can be
93 : * obtained right now, but more may be forthcoming later. In this situation,
94 : * the decompressor will return to its caller (with an indication of the
95 : * number of scanlines it has read, if any). The application should resume
96 : * decompression after it has loaded more data into the input buffer. Note
97 : * that there are substantial restrictions on the use of suspension --- see
98 : * the documentation.
99 : *
100 : * When suspending, the decompressor will back up to a convenient restart point
101 : * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
102 : * indicate where the restart point will be if the current call returns FALSE.
103 : * Data beyond this point must be rescanned after resumption, so move it to
104 : * the front of the buffer rather than discarding it.
105 : */
106 :
107 : METHODDEF(boolean)
108 757 : fill_input_buffer (j_decompress_ptr cinfo)
109 : {
110 757 : my_src_ptr src = (my_src_ptr) cinfo->src;
111 : size_t nbytes;
112 :
113 757 : nbytes = VSIFReadL(src->buffer, 1, INPUT_BUF_SIZE, src->infile);
114 :
115 757 : if (nbytes <= 0) {
116 0 : if (src->start_of_file) /* Treat empty input file as fatal error */
117 0 : ERREXIT(cinfo, JERR_INPUT_EMPTY);
118 0 : WARNMS(cinfo, JWRN_JPEG_EOF);
119 : /* Insert a fake EOI marker */
120 0 : src->buffer[0] = (JOCTET) 0xFF;
121 0 : src->buffer[1] = (JOCTET) JPEG_EOI;
122 0 : nbytes = 2;
123 : }
124 :
125 757 : src->pub.next_input_byte = src->buffer;
126 757 : src->pub.bytes_in_buffer = nbytes;
127 757 : src->start_of_file = FALSE;
128 :
129 757 : return TRUE;
130 : }
131 :
132 : /*
133 : * The Intel IPP performance libraries do not necessarily read the
134 : * entire contents of the buffer with each pass, so each re-fill
135 : * copies the remaining buffer bytes to the front of the buffer,
136 : * then fills up the rest with new data.
137 : */
138 : #ifdef IPPJ_HUFF
139 : METHODDEF(boolean)
140 : fill_input_buffer_ipp (j_decompress_ptr cinfo)
141 : {
142 : my_src_ptr src = (my_src_ptr) cinfo->src;
143 : size_t bytes_left = src->pub.bytes_in_buffer;
144 : size_t bytes_to_read = INPUT_BUF_SIZE - bytes_left;
145 : size_t nbytes;
146 :
147 : if(src->start_of_file || cinfo->progressive_mode)
148 : {
149 : return fill_input_buffer(cinfo);
150 : }
151 :
152 : memmove(src->buffer,src->pub.next_input_byte,bytes_left);
153 :
154 : nbytes = VSIFReadL(src->buffer + bytes_left, 1, bytes_to_read, src->infile);
155 :
156 : if(nbytes <= 0)
157 : {
158 : if(src->start_of_file)
159 : {
160 : /* Treat empty input file as fatal error */
161 : ERREXIT(cinfo, JERR_INPUT_EMPTY);
162 : }
163 :
164 : if(src->pub.bytes_in_buffer == 0 && cinfo->unread_marker == 0)
165 : {
166 : WARNMS(cinfo, JWRN_JPEG_EOF);
167 :
168 : /* Insert a fake EOI marker */
169 : src->buffer[0] = (JOCTET)0xFF;
170 : src->buffer[1] = (JOCTET)JPEG_EOI;
171 : nbytes = 2;
172 : }
173 :
174 : src->pub.next_input_byte = src->buffer;
175 : src->pub.bytes_in_buffer = bytes_left + nbytes;
176 : src->start_of_file = FALSE;
177 :
178 : return TRUE;
179 : }
180 :
181 : src->pub.next_input_byte = src->buffer;
182 : src->pub.bytes_in_buffer = bytes_left + nbytes;
183 : src->start_of_file = FALSE;
184 :
185 : return TRUE;
186 : }
187 : #endif /* IPPJ_HUFF */
188 :
189 :
190 : /*
191 : * Skip data --- used to skip over a potentially large amount of
192 : * uninteresting data (such as an APPn marker).
193 : *
194 : * Writers of suspendable-input applications must note that skip_input_data
195 : * is not granted the right to give a suspension return. If the skip extends
196 : * beyond the data currently in the buffer, the buffer can be marked empty so
197 : * that the next read will cause a fill_input_buffer call that can suspend.
198 : * Arranging for additional bytes to be discarded before reloading the input
199 : * buffer is the application writer's problem.
200 : */
201 :
202 : METHODDEF(void)
203 31 : skip_input_data (j_decompress_ptr cinfo, long num_bytes)
204 : {
205 31 : my_src_ptr src = (my_src_ptr) cinfo->src;
206 :
207 : /* Just a dumb implementation for now. Could use fseek() except
208 : * it doesn't work on pipes. Not clear that being smart is worth
209 : * any trouble anyway --- large skips are infrequent.
210 : */
211 31 : if (num_bytes > 0) {
212 62 : while (num_bytes > (long) src->pub.bytes_in_buffer) {
213 0 : num_bytes -= (long) src->pub.bytes_in_buffer;
214 0 : (void) fill_input_buffer(cinfo);
215 : /* note we assume that fill_input_buffer will never return FALSE,
216 : * so suspension need not be handled.
217 : */
218 : }
219 31 : src->pub.next_input_byte += (size_t) num_bytes;
220 31 : src->pub.bytes_in_buffer -= (size_t) num_bytes;
221 : }
222 31 : }
223 :
224 :
225 : /*
226 : * An additional method that can be provided by data source modules is the
227 : * resync_to_restart method for error recovery in the presence of RST markers.
228 : * For the moment, this source module just uses the default resync method
229 : * provided by the JPEG library. That method assumes that no backtracking
230 : * is possible.
231 : */
232 :
233 :
234 : /*
235 : * Terminate source --- called by jpeg_finish_decompress
236 : * after all data has been read. Often a no-op.
237 : *
238 : * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
239 : * application must deal with any cleanup that should happen even
240 : * for error exit.
241 : */
242 :
243 : METHODDEF(void)
244 0 : term_source (j_decompress_ptr cinfo)
245 : {
246 : /* no work necessary here */
247 0 : }
248 :
249 :
250 : /*
251 : * Prepare for input from a stdio stream.
252 : * The caller must have already opened the stream, and is responsible
253 : * for closing it after finishing decompression.
254 : */
255 :
256 120 : void jpeg_vsiio_src (j_decompress_ptr cinfo, FILE * infile)
257 : {
258 : my_src_ptr src;
259 :
260 : /* The source object and input buffer are made permanent so that a series
261 : * of JPEG images can be read from the same file by calling jpeg_stdio_src
262 : * only before the first one. (If we discarded the buffer at the end of
263 : * one image, we'd likely lose the start of the next one.)
264 : * This makes it unsafe to use this manager and a different source
265 : * manager serially with the same JPEG object. Caveat programmer.
266 : */
267 120 : if (cinfo->src == NULL) { /* first time for this JPEG object? */
268 : cinfo->src = (struct jpeg_source_mgr *)
269 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
270 120 : sizeof(my_source_mgr));
271 120 : src = (my_src_ptr) cinfo->src;
272 : src->buffer = (JOCTET *)
273 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
274 120 : INPUT_BUF_SIZE * sizeof(JOCTET));
275 : }
276 :
277 120 : src = (my_src_ptr) cinfo->src;
278 120 : src->pub.init_source = init_source;
279 : #ifdef IPPJ_HUFF
280 : src->pub.fill_input_buffer = fill_input_buffer_ipp;
281 : #else
282 120 : src->pub.fill_input_buffer = fill_input_buffer;
283 : #endif
284 120 : src->pub.skip_input_data = skip_input_data;
285 120 : src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
286 120 : src->pub.term_source = term_source;
287 120 : src->infile = infile;
288 120 : src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
289 120 : src->pub.next_input_byte = NULL; /* until buffer loaded */
290 120 : }
291 :
292 :
293 : /* ==================================================================== */
294 : /* The rest was derived from jdatadst.c */
295 : /* ==================================================================== */
296 :
297 : /* Expanded data destination object for stdio output */
298 :
299 : typedef struct {
300 : struct jpeg_destination_mgr pub; /* public fields */
301 :
302 : FILE * outfile; /* target stream */
303 : JOCTET * buffer; /* start of buffer */
304 : } my_destination_mgr;
305 :
306 : typedef my_destination_mgr * my_dest_ptr;
307 :
308 : #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */
309 :
310 :
311 : /*
312 : * Initialize destination --- called by jpeg_start_compress
313 : * before any data is actually written.
314 : */
315 :
316 : METHODDEF(void)
317 18 : init_destination (j_compress_ptr cinfo)
318 : {
319 18 : my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
320 :
321 : /* Allocate the output buffer --- it will be released when done with image */
322 : dest->buffer = (JOCTET *)
323 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
324 18 : OUTPUT_BUF_SIZE * sizeof(JOCTET));
325 :
326 18 : dest->pub.next_output_byte = dest->buffer;
327 18 : dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
328 18 : }
329 :
330 :
331 : /*
332 : * Empty the output buffer --- called whenever buffer fills up.
333 : *
334 : * In typical applications, this should write the entire output buffer
335 : * (ignoring the current state of next_output_byte & free_in_buffer),
336 : * reset the pointer & count to the start of the buffer, and return TRUE
337 : * indicating that the buffer has been dumped.
338 : *
339 : * In applications that need to be able to suspend compression due to output
340 : * overrun, a FALSE return indicates that the buffer cannot be emptied now.
341 : * In this situation, the compressor will return to its caller (possibly with
342 : * an indication that it has not accepted all the supplied scanlines). The
343 : * application should resume compression after it has made more room in the
344 : * output buffer. Note that there are substantial restrictions on the use of
345 : * suspension --- see the documentation.
346 : *
347 : * When suspending, the compressor will back up to a convenient restart point
348 : * (typically the start of the current MCU). next_output_byte & free_in_buffer
349 : * indicate where the restart point will be if the current call returns FALSE.
350 : * Data beyond this point will be regenerated after resumption, so do not
351 : * write it out when emptying the buffer externally.
352 : */
353 :
354 : METHODDEF(boolean)
355 1 : empty_output_buffer (j_compress_ptr cinfo)
356 : {
357 1 : my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
358 1 : size_t bytes_to_write = OUTPUT_BUF_SIZE;
359 :
360 : #ifdef IPPJ_HUFF
361 : /*
362 : * The Intel IPP performance libraries do not necessarily fill up
363 : * the whole output buffer with each compression pass, so we only
364 : * want to write out the parts of the buffer that are full.
365 : */
366 : if(! cinfo->progressive_mode) {
367 : bytes_to_write -= dest->pub.free_in_buffer;
368 : }
369 : #endif
370 :
371 1 : if (VSIFWriteL(dest->buffer, 1, bytes_to_write, dest->outfile) != bytes_to_write)
372 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
373 :
374 1 : dest->pub.next_output_byte = dest->buffer;
375 1 : dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
376 :
377 1 : return TRUE;
378 : }
379 :
380 : /*
381 : * Terminate destination --- called by jpeg_finish_compress
382 : * after all data has been written. Usually needs to flush buffer.
383 : *
384 : * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
385 : * application must deal with any cleanup that should happen even
386 : * for error exit.
387 : */
388 :
389 : METHODDEF(void)
390 18 : term_destination (j_compress_ptr cinfo)
391 : {
392 18 : my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
393 18 : size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
394 :
395 : /* Write any data remaining in the buffer */
396 18 : if (datacount > 0) {
397 18 : if (VSIFWriteL(dest->buffer, 1, datacount, dest->outfile) != datacount)
398 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
399 : }
400 18 : if( VSIFFlushL(dest->outfile) != 0 )
401 0 : ERREXIT(cinfo, JERR_FILE_WRITE);
402 18 : }
403 :
404 :
405 : /*
406 : * Prepare for output to a stdio stream.
407 : * The caller must have already opened the stream, and is responsible
408 : * for closing it after finishing compression.
409 : */
410 :
411 : void
412 18 : jpeg_vsiio_dest (j_compress_ptr cinfo, FILE * outfile)
413 : {
414 : my_dest_ptr dest;
415 :
416 : /* The destination object is made permanent so that multiple JPEG images
417 : * can be written to the same file without re-executing jpeg_stdio_dest.
418 : * This makes it dangerous to use this manager and a different destination
419 : * manager serially with the same JPEG object, because their private object
420 : * sizes may be different. Caveat programmer.
421 : */
422 18 : if (cinfo->dest == NULL) { /* first time for this JPEG object? */
423 : cinfo->dest = (struct jpeg_destination_mgr *)
424 : (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
425 18 : sizeof(my_destination_mgr));
426 : }
427 :
428 18 : dest = (my_dest_ptr) cinfo->dest;
429 18 : dest->pub.init_destination = init_destination;
430 18 : dest->pub.empty_output_buffer = empty_output_buffer;
431 18 : dest->pub.term_destination = term_destination;
432 18 : dest->outfile = outfile;
433 18 : }
|