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