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