/src/freeimage-svn/FreeImage/trunk/Source/FreeImage/PluginJPEG.cpp
Line | Count | Source |
1 | | // ========================================================== |
2 | | // JPEG Loader and writer |
3 | | // Based on code developed by The Independent JPEG Group |
4 | | // |
5 | | // Design and implementation by |
6 | | // - Floris van den Berg (flvdberg@wxs.nl) |
7 | | // - Jan L. Nauta (jln@magentammt.com) |
8 | | // - Markus Loibl (markus.loibl@epost.de) |
9 | | // - Karl-Heinz Bussian (khbussian@moss.de) |
10 | | // - Hervé Drolon (drolon@infonie.fr) |
11 | | // - Jascha Wetzel (jascha@mainia.de) |
12 | | // - Mihail Naydenov (mnaydenov@users.sourceforge.net) |
13 | | // |
14 | | // This file is part of FreeImage 3 |
15 | | // |
16 | | // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY |
17 | | // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES |
18 | | // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE |
19 | | // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED |
20 | | // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT |
21 | | // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY |
22 | | // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL |
23 | | // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER |
24 | | // THIS DISCLAIMER. |
25 | | // |
26 | | // Use at your own risk! |
27 | | // ========================================================== |
28 | | |
29 | | #ifdef _MSC_VER |
30 | | #pragma warning (disable : 4786) // identifier was truncated to 'number' characters |
31 | | #endif |
32 | | |
33 | | extern "C" { |
34 | | #define XMD_H |
35 | | #undef FAR |
36 | | #include <setjmp.h> |
37 | | |
38 | | #include "../LibJPEG/jinclude.h" |
39 | | #include "../LibJPEG/jpeglib.h" |
40 | | #include "../LibJPEG/jerror.h" |
41 | | } |
42 | | |
43 | | #include "FreeImage.h" |
44 | | #include "Utilities.h" |
45 | | |
46 | | #include "../Metadata/FreeImageTag.h" |
47 | | |
48 | | |
49 | | // ========================================================== |
50 | | // Plugin Interface |
51 | | // ========================================================== |
52 | | |
53 | | static int s_format_id; |
54 | | |
55 | | // ---------------------------------------------------------- |
56 | | // Constant declarations |
57 | | // ---------------------------------------------------------- |
58 | | |
59 | 0 | #define INPUT_BUF_SIZE 4096 // choose an efficiently fread'able size |
60 | 0 | #define OUTPUT_BUF_SIZE 4096 // choose an efficiently fwrite'able size |
61 | | |
62 | 0 | #define EXIF_MARKER (JPEG_APP0+1) // EXIF marker / Adobe XMP marker |
63 | 0 | #define ICC_MARKER (JPEG_APP0+2) // ICC profile marker |
64 | 0 | #define IPTC_MARKER (JPEG_APP0+13) // IPTC marker / BIM marker |
65 | | |
66 | 0 | #define ICC_HEADER_SIZE 14 // size of non-profile data in APP2 |
67 | 0 | #define MAX_BYTES_IN_MARKER 65533L // maximum data length of a JPEG marker |
68 | 0 | #define MAX_DATA_BYTES_IN_MARKER 65519L // maximum data length of a JPEG APP2 marker |
69 | | |
70 | 0 | #define MAX_JFXX_THUMB_SIZE (MAX_BYTES_IN_MARKER - 5 - 1) |
71 | | |
72 | 0 | #define JFXX_TYPE_JPEG 0x10 // JFIF extension marker: JPEG-compressed thumbnail image |
73 | 0 | #define JFXX_TYPE_8bit 0x11 // JFIF extension marker: palette thumbnail image |
74 | 0 | #define JFXX_TYPE_24bit 0x13 // JFIF extension marker: RGB thumbnail image |
75 | | |
76 | | // ---------------------------------------------------------- |
77 | | // Typedef declarations |
78 | | // ---------------------------------------------------------- |
79 | | |
80 | | typedef struct tagErrorManager { |
81 | | /// "public" fields |
82 | | struct jpeg_error_mgr pub; |
83 | | /// for return to caller |
84 | | jmp_buf setjmp_buffer; |
85 | | } ErrorManager; |
86 | | |
87 | | typedef struct tagSourceManager { |
88 | | /// public fields |
89 | | struct jpeg_source_mgr pub; |
90 | | /// source stream |
91 | | fi_handle infile; |
92 | | FreeImageIO *m_io; |
93 | | /// start of buffer |
94 | | JOCTET * buffer; |
95 | | /// have we gotten any data yet ? |
96 | | boolean start_of_file; |
97 | | } SourceManager; |
98 | | |
99 | | typedef struct tagDestinationManager { |
100 | | /// public fields |
101 | | struct jpeg_destination_mgr pub; |
102 | | /// destination stream |
103 | | fi_handle outfile; |
104 | | FreeImageIO *m_io; |
105 | | /// start of buffer |
106 | | JOCTET * buffer; |
107 | | } DestinationManager; |
108 | | |
109 | | typedef SourceManager* freeimage_src_ptr; |
110 | | typedef DestinationManager* freeimage_dst_ptr; |
111 | | typedef ErrorManager* freeimage_error_ptr; |
112 | | |
113 | | // ---------------------------------------------------------- |
114 | | // Error handling |
115 | | // ---------------------------------------------------------- |
116 | | |
117 | | /** Fatal errors (print message and exit) */ |
118 | | static inline void |
119 | 0 | JPEG_EXIT(j_common_ptr cinfo, int code) { |
120 | 0 | freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err; |
121 | 0 | error_ptr->pub.msg_code = code; |
122 | 0 | error_ptr->pub.error_exit(cinfo); |
123 | 0 | } |
124 | | |
125 | | /** Nonfatal errors (we can keep going, but the data is probably corrupt) */ |
126 | | static inline void |
127 | 0 | JPEG_WARNING(j_common_ptr cinfo, int code) { |
128 | 0 | freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err; |
129 | 0 | error_ptr->pub.msg_code = code; |
130 | 0 | error_ptr->pub.emit_message(cinfo, -1); |
131 | 0 | } |
132 | | |
133 | | /** |
134 | | Receives control for a fatal error. Information sufficient to |
135 | | generate the error message has been stored in cinfo->err; call |
136 | | output_message to display it. Control must NOT return to the caller; |
137 | | generally this routine will exit() or longjmp() somewhere. |
138 | | */ |
139 | | METHODDEF(void) |
140 | 0 | jpeg_error_exit (j_common_ptr cinfo) { |
141 | 0 | freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err; |
142 | | |
143 | | // always display the message |
144 | 0 | error_ptr->pub.output_message(cinfo); |
145 | | |
146 | | // allow JPEG with unknown markers |
147 | 0 | if(error_ptr->pub.msg_code != JERR_UNKNOWN_MARKER) { |
148 | | |
149 | | // let the memory manager delete any temp files before we die |
150 | 0 | jpeg_destroy(cinfo); |
151 | | |
152 | | // return control to the setjmp point |
153 | 0 | longjmp(error_ptr->setjmp_buffer, 1); |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | | /** |
158 | | Actual output of any JPEG message. Note that this method does not know |
159 | | how to generate a message, only where to send it. |
160 | | */ |
161 | | METHODDEF(void) |
162 | 0 | jpeg_output_message (j_common_ptr cinfo) { |
163 | 0 | char buffer[JMSG_LENGTH_MAX]; |
164 | 0 | freeimage_error_ptr error_ptr = (freeimage_error_ptr)cinfo->err; |
165 | | |
166 | | // create the message |
167 | 0 | error_ptr->pub.format_message(cinfo, buffer); |
168 | | // send it to user's message proc |
169 | 0 | FreeImage_OutputMessageProc(s_format_id, buffer); |
170 | 0 | } |
171 | | |
172 | | // ---------------------------------------------------------- |
173 | | // Destination manager |
174 | | // ---------------------------------------------------------- |
175 | | |
176 | | /** |
177 | | Initialize destination. This is called by jpeg_start_compress() |
178 | | before any data is actually written. It must initialize |
179 | | next_output_byte and free_in_buffer. free_in_buffer must be |
180 | | initialized to a positive value. |
181 | | */ |
182 | | METHODDEF(void) |
183 | 0 | init_destination (j_compress_ptr cinfo) { |
184 | 0 | freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest; |
185 | |
|
186 | 0 | dest->buffer = (JOCTET *) |
187 | 0 | (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, |
188 | 0 | OUTPUT_BUF_SIZE * sizeof(JOCTET)); |
189 | |
|
190 | 0 | dest->pub.next_output_byte = dest->buffer; |
191 | 0 | dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; |
192 | 0 | } |
193 | | |
194 | | /** |
195 | | This is called whenever the buffer has filled (free_in_buffer |
196 | | reaches zero). In typical applications, it should write out the |
197 | | *entire* buffer (use the saved start address and buffer length; |
198 | | ignore the current state of next_output_byte and free_in_buffer). |
199 | | Then reset the pointer & count to the start of the buffer, and |
200 | | return TRUE indicating that the buffer has been dumped. |
201 | | free_in_buffer must be set to a positive value when TRUE is |
202 | | returned. A FALSE return should only be used when I/O suspension is |
203 | | desired. |
204 | | */ |
205 | | METHODDEF(boolean) |
206 | 0 | empty_output_buffer (j_compress_ptr cinfo) { |
207 | 0 | freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest; |
208 | |
|
209 | 0 | if (dest->m_io->write_proc(dest->buffer, 1, OUTPUT_BUF_SIZE, dest->outfile) != OUTPUT_BUF_SIZE) { |
210 | | // let the memory manager delete any temp files before we die |
211 | 0 | jpeg_destroy((j_common_ptr)cinfo); |
212 | |
|
213 | 0 | JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE); |
214 | 0 | } |
215 | |
|
216 | 0 | dest->pub.next_output_byte = dest->buffer; |
217 | 0 | dest->pub.free_in_buffer = OUTPUT_BUF_SIZE; |
218 | |
|
219 | 0 | return TRUE; |
220 | 0 | } |
221 | | |
222 | | /** |
223 | | Terminate destination --- called by jpeg_finish_compress() after all |
224 | | data has been written. In most applications, this must flush any |
225 | | data remaining in the buffer. Use either next_output_byte or |
226 | | free_in_buffer to determine how much data is in the buffer. |
227 | | */ |
228 | | METHODDEF(void) |
229 | 0 | term_destination (j_compress_ptr cinfo) { |
230 | 0 | freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest; |
231 | |
|
232 | 0 | size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer; |
233 | | |
234 | | // write any data remaining in the buffer |
235 | |
|
236 | 0 | if (datacount > 0) { |
237 | 0 | if (dest->m_io->write_proc(dest->buffer, 1, (unsigned int)datacount, dest->outfile) != datacount) { |
238 | | // let the memory manager delete any temp files before we die |
239 | 0 | jpeg_destroy((j_common_ptr)cinfo); |
240 | | |
241 | 0 | JPEG_EXIT((j_common_ptr)cinfo, JERR_FILE_WRITE); |
242 | 0 | } |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | // ---------------------------------------------------------- |
247 | | // Source manager |
248 | | // ---------------------------------------------------------- |
249 | | |
250 | | /** |
251 | | Initialize source. This is called by jpeg_read_header() before any |
252 | | data is actually read. Unlike init_destination(), it may leave |
253 | | bytes_in_buffer set to 0 (in which case a fill_input_buffer() call |
254 | | will occur immediately). |
255 | | */ |
256 | | METHODDEF(void) |
257 | 0 | init_source (j_decompress_ptr cinfo) { |
258 | 0 | freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src; |
259 | | |
260 | | /* We reset the empty-input-file flag for each image, |
261 | | * but we don't clear the input buffer. |
262 | | * This is correct behavior for reading a series of images from one source. |
263 | | */ |
264 | |
|
265 | 0 | src->start_of_file = TRUE; |
266 | 0 | } |
267 | | |
268 | | /** |
269 | | This is called whenever bytes_in_buffer has reached zero and more |
270 | | data is wanted. In typical applications, it should read fresh data |
271 | | into the buffer (ignoring the current state of next_input_byte and |
272 | | bytes_in_buffer), reset the pointer & count to the start of the |
273 | | buffer, and return TRUE indicating that the buffer has been reloaded. |
274 | | It is not necessary to fill the buffer entirely, only to obtain at |
275 | | least one more byte. bytes_in_buffer MUST be set to a positive value |
276 | | if TRUE is returned. A FALSE return should only be used when I/O |
277 | | suspension is desired. |
278 | | */ |
279 | | METHODDEF(boolean) |
280 | 0 | fill_input_buffer (j_decompress_ptr cinfo) { |
281 | 0 | freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src; |
282 | |
|
283 | 0 | size_t nbytes = src->m_io->read_proc(src->buffer, 1, INPUT_BUF_SIZE, src->infile); |
284 | |
|
285 | 0 | if (nbytes <= 0) { |
286 | 0 | if (src->start_of_file) { |
287 | | // treat empty input file as fatal error |
288 | | |
289 | | // let the memory manager delete any temp files before we die |
290 | 0 | jpeg_destroy((j_common_ptr)cinfo); |
291 | |
|
292 | 0 | JPEG_EXIT((j_common_ptr)cinfo, JERR_INPUT_EMPTY); |
293 | 0 | } |
294 | |
|
295 | 0 | JPEG_WARNING((j_common_ptr)cinfo, JWRN_JPEG_EOF); |
296 | | |
297 | | /* Insert a fake EOI marker */ |
298 | |
|
299 | 0 | src->buffer[0] = (JOCTET) 0xFF; |
300 | 0 | src->buffer[1] = (JOCTET) JPEG_EOI; |
301 | |
|
302 | 0 | nbytes = 2; |
303 | 0 | } |
304 | |
|
305 | 0 | src->pub.next_input_byte = src->buffer; |
306 | 0 | src->pub.bytes_in_buffer = nbytes; |
307 | 0 | src->start_of_file = FALSE; |
308 | |
|
309 | 0 | return TRUE; |
310 | 0 | } |
311 | | |
312 | | /** |
313 | | Skip num_bytes worth of data. The buffer pointer and count should |
314 | | be advanced over num_bytes input bytes, refilling the buffer as |
315 | | needed. This is used to skip over a potentially large amount of |
316 | | uninteresting data (such as an APPn marker). In some applications |
317 | | it may be possible to optimize away the reading of the skipped data, |
318 | | but it's not clear that being smart is worth much trouble; large |
319 | | skips are uncommon. bytes_in_buffer may be zero on return. |
320 | | A zero or negative skip count should be treated as a no-op. |
321 | | */ |
322 | | METHODDEF(void) |
323 | 0 | skip_input_data (j_decompress_ptr cinfo, long num_bytes) { |
324 | 0 | freeimage_src_ptr src = (freeimage_src_ptr) cinfo->src; |
325 | | |
326 | | /* Just a dumb implementation for now. Could use fseek() except |
327 | | * it doesn't work on pipes. Not clear that being smart is worth |
328 | | * any trouble anyway --- large skips are infrequent. |
329 | | */ |
330 | |
|
331 | 0 | if (num_bytes > 0) { |
332 | 0 | while (num_bytes > (long) src->pub.bytes_in_buffer) { |
333 | 0 | num_bytes -= (long) src->pub.bytes_in_buffer; |
334 | |
|
335 | 0 | (void) fill_input_buffer(cinfo); |
336 | | |
337 | | /* note we assume that fill_input_buffer will never return FALSE, |
338 | | * so suspension need not be handled. |
339 | | */ |
340 | 0 | } |
341 | |
|
342 | 0 | src->pub.next_input_byte += (size_t) num_bytes; |
343 | 0 | src->pub.bytes_in_buffer -= (size_t) num_bytes; |
344 | 0 | } |
345 | 0 | } |
346 | | |
347 | | /** |
348 | | Terminate source --- called by jpeg_finish_decompress |
349 | | after all data has been read. Often a no-op. |
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 | | */ |
355 | | METHODDEF(void) |
356 | 0 | term_source (j_decompress_ptr cinfo) { |
357 | | // no work necessary here |
358 | 0 | } |
359 | | |
360 | | // ---------------------------------------------------------- |
361 | | // Source manager & Destination manager setup |
362 | | // ---------------------------------------------------------- |
363 | | |
364 | | /** |
365 | | Prepare for input from a stdio stream. |
366 | | The caller must have already opened the stream, and is responsible |
367 | | for closing it after finishing decompression. |
368 | | */ |
369 | | GLOBAL(void) |
370 | 0 | jpeg_freeimage_src (j_decompress_ptr cinfo, fi_handle infile, FreeImageIO *io) { |
371 | 0 | freeimage_src_ptr src; |
372 | | |
373 | | // allocate memory for the buffer. is released automatically in the end |
374 | |
|
375 | 0 | if (cinfo->src == NULL) { |
376 | 0 | cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) |
377 | 0 | ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(SourceManager)); |
378 | |
|
379 | 0 | src = (freeimage_src_ptr) cinfo->src; |
380 | |
|
381 | 0 | src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) |
382 | 0 | ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET)); |
383 | 0 | } |
384 | | |
385 | | // initialize the jpeg pointer struct with pointers to functions |
386 | |
|
387 | 0 | src = (freeimage_src_ptr) cinfo->src; |
388 | 0 | src->pub.init_source = init_source; |
389 | 0 | src->pub.fill_input_buffer = fill_input_buffer; |
390 | 0 | src->pub.skip_input_data = skip_input_data; |
391 | 0 | src->pub.resync_to_restart = jpeg_resync_to_restart; // use default method |
392 | 0 | src->pub.term_source = term_source; |
393 | 0 | src->infile = infile; |
394 | 0 | src->m_io = io; |
395 | 0 | src->pub.bytes_in_buffer = 0; // forces fill_input_buffer on first read |
396 | 0 | src->pub.next_input_byte = NULL; // until buffer loaded |
397 | 0 | } |
398 | | |
399 | | /** |
400 | | Prepare for output to a stdio stream. |
401 | | The caller must have already opened the stream, and is responsible |
402 | | for closing it after finishing compression. |
403 | | */ |
404 | | GLOBAL(void) |
405 | 0 | jpeg_freeimage_dst (j_compress_ptr cinfo, fi_handle outfile, FreeImageIO *io) { |
406 | 0 | freeimage_dst_ptr dest; |
407 | |
|
408 | 0 | if (cinfo->dest == NULL) { |
409 | 0 | cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small) |
410 | 0 | ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(DestinationManager)); |
411 | 0 | } |
412 | |
|
413 | 0 | dest = (freeimage_dst_ptr) cinfo->dest; |
414 | 0 | dest->pub.init_destination = init_destination; |
415 | 0 | dest->pub.empty_output_buffer = empty_output_buffer; |
416 | 0 | dest->pub.term_destination = term_destination; |
417 | 0 | dest->outfile = outfile; |
418 | 0 | dest->m_io = io; |
419 | 0 | } |
420 | | |
421 | | // ---------------------------------------------------------- |
422 | | // Special markers read functions |
423 | | // ---------------------------------------------------------- |
424 | | |
425 | | /** |
426 | | Read JPEG_COM marker (comment) |
427 | | */ |
428 | | static BOOL |
429 | 0 | jpeg_read_comment(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
430 | 0 | size_t length = datalen; |
431 | 0 | BYTE *profile = (BYTE*)dataptr; |
432 | | |
433 | | // read the comment |
434 | 0 | char *value = (char*)malloc((length + 1) * sizeof(char)); |
435 | 0 | if(value == NULL) return FALSE; |
436 | 0 | memcpy(value, profile, length); |
437 | 0 | value[length] = '\0'; |
438 | | |
439 | | // create a tag |
440 | 0 | FITAG *tag = FreeImage_CreateTag(); |
441 | 0 | if(tag) { |
442 | 0 | unsigned int count = (unsigned int)length + 1; // includes the null value |
443 | |
|
444 | 0 | FreeImage_SetTagID(tag, JPEG_COM); |
445 | 0 | FreeImage_SetTagKey(tag, "Comment"); |
446 | 0 | FreeImage_SetTagLength(tag, count); |
447 | 0 | FreeImage_SetTagCount(tag, count); |
448 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
449 | 0 | FreeImage_SetTagValue(tag, value); |
450 | | |
451 | | // store the tag |
452 | 0 | FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); |
453 | | |
454 | | // destroy the tag |
455 | 0 | FreeImage_DeleteTag(tag); |
456 | 0 | } |
457 | |
|
458 | 0 | free(value); |
459 | |
|
460 | 0 | return TRUE; |
461 | 0 | } |
462 | | |
463 | | /** |
464 | | Read JPEG_APP2 marker (ICC profile) |
465 | | */ |
466 | | |
467 | | /** |
468 | | Handy subroutine to test whether a saved marker is an ICC profile marker. |
469 | | */ |
470 | | static BOOL |
471 | 0 | marker_is_icc(jpeg_saved_marker_ptr marker) { |
472 | | // marker identifying string "ICC_PROFILE" (null-terminated) |
473 | 0 | const BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 }; |
474 | |
|
475 | 0 | if(marker->marker == ICC_MARKER) { |
476 | | // verify the identifying string |
477 | 0 | if(marker->data_length >= ICC_HEADER_SIZE) { |
478 | 0 | if(memcmp(icc_signature, marker->data, sizeof(icc_signature)) == 0) { |
479 | 0 | return TRUE; |
480 | 0 | } |
481 | 0 | } |
482 | 0 | } |
483 | | |
484 | 0 | return FALSE; |
485 | 0 | } |
486 | | |
487 | | /** |
488 | | See if there was an ICC profile in the JPEG file being read; |
489 | | if so, reassemble and return the profile data. |
490 | | |
491 | | TRUE is returned if an ICC profile was found, FALSE if not. |
492 | | If TRUE is returned, *icc_data_ptr is set to point to the |
493 | | returned data, and *icc_data_len is set to its length. |
494 | | |
495 | | IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() |
496 | | and must be freed by the caller with free() when the caller no longer |
497 | | needs it. (Alternatively, we could write this routine to use the |
498 | | IJG library's memory allocator, so that the data would be freed implicitly |
499 | | at jpeg_finish_decompress() time. But it seems likely that many apps |
500 | | will prefer to have the data stick around after decompression finishes.) |
501 | | |
502 | | NOTE: if the file contains invalid ICC APP2 markers, we just silently |
503 | | return FALSE. You might want to issue an error message instead. |
504 | | */ |
505 | | static BOOL |
506 | 0 | jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned *icc_data_len) { |
507 | 0 | jpeg_saved_marker_ptr marker; |
508 | 0 | int num_markers = 0; |
509 | 0 | int seq_no; |
510 | 0 | JOCTET *icc_data; |
511 | 0 | unsigned total_length; |
512 | |
|
513 | 0 | const int MAX_SEQ_NO = 255; // sufficient since marker numbers are bytes |
514 | 0 | BYTE marker_present[MAX_SEQ_NO+1]; // 1 if marker found |
515 | 0 | unsigned data_length[MAX_SEQ_NO+1]; // size of profile data in marker |
516 | 0 | unsigned data_offset[MAX_SEQ_NO+1]; // offset for data in marker |
517 | | |
518 | 0 | *icc_data_ptr = NULL; // avoid confusion if FALSE return |
519 | 0 | *icc_data_len = 0; |
520 | | |
521 | | /** |
522 | | this first pass over the saved markers discovers whether there are |
523 | | any ICC markers and verifies the consistency of the marker numbering. |
524 | | */ |
525 | | |
526 | 0 | memset(marker_present, 0, (MAX_SEQ_NO + 1)); |
527 | | |
528 | 0 | for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) { |
529 | 0 | if (marker_is_icc(marker)) { |
530 | 0 | if (num_markers == 0) { |
531 | | // number of markers |
532 | 0 | num_markers = GETJOCTET(marker->data[13]); |
533 | 0 | } |
534 | 0 | else if (num_markers != GETJOCTET(marker->data[13])) { |
535 | 0 | return FALSE; // inconsistent num_markers fields |
536 | 0 | } |
537 | | // sequence number |
538 | 0 | seq_no = GETJOCTET(marker->data[12]); |
539 | 0 | if (seq_no <= 0 || seq_no > num_markers) { |
540 | 0 | return FALSE; // bogus sequence number |
541 | 0 | } |
542 | 0 | if (marker_present[seq_no]) { |
543 | 0 | return FALSE; // duplicate sequence numbers |
544 | 0 | } |
545 | 0 | marker_present[seq_no] = 1; |
546 | 0 | data_length[seq_no] = marker->data_length - ICC_HEADER_SIZE; |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | 0 | if (num_markers == 0) |
551 | 0 | return FALSE; |
552 | | |
553 | | /** |
554 | | check for missing markers, count total space needed, |
555 | | compute offset of each marker's part of the data. |
556 | | */ |
557 | | |
558 | 0 | total_length = 0; |
559 | 0 | for(seq_no = 1; seq_no <= num_markers; seq_no++) { |
560 | 0 | if (marker_present[seq_no] == 0) { |
561 | 0 | return FALSE; // missing sequence number |
562 | 0 | } |
563 | 0 | data_offset[seq_no] = total_length; |
564 | 0 | total_length += data_length[seq_no]; |
565 | 0 | } |
566 | | |
567 | 0 | if (total_length <= 0) |
568 | 0 | return FALSE; // found only empty markers ? |
569 | | |
570 | | // allocate space for assembled data |
571 | 0 | icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); |
572 | 0 | if (icc_data == NULL) |
573 | 0 | return FALSE; // out of memory |
574 | | |
575 | | // and fill it in |
576 | 0 | for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { |
577 | 0 | if (marker_is_icc(marker)) { |
578 | 0 | JOCTET FAR *src_ptr; |
579 | 0 | JOCTET *dst_ptr; |
580 | 0 | unsigned length; |
581 | 0 | seq_no = GETJOCTET(marker->data[12]); |
582 | 0 | dst_ptr = icc_data + data_offset[seq_no]; |
583 | 0 | src_ptr = marker->data + ICC_HEADER_SIZE; |
584 | 0 | length = data_length[seq_no]; |
585 | 0 | while (length--) { |
586 | 0 | *dst_ptr++ = *src_ptr++; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | } |
590 | | |
591 | 0 | *icc_data_ptr = icc_data; |
592 | 0 | *icc_data_len = total_length; |
593 | | |
594 | 0 | return TRUE; |
595 | 0 | } |
596 | | |
597 | | /** |
598 | | Read JPEG_APPD marker (IPTC or Adobe Photoshop profile) |
599 | | */ |
600 | | static BOOL |
601 | 0 | jpeg_read_iptc_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
602 | 0 | return read_iptc_profile(dib, dataptr, datalen); |
603 | 0 | } |
604 | | |
605 | | /** |
606 | | Read JPEG_APP1 marker (XMP profile) |
607 | | @param dib Input FIBITMAP |
608 | | @param dataptr Pointer to the APP1 marker |
609 | | @param datalen APP1 marker length |
610 | | @return Returns TRUE if successful, FALSE otherwise |
611 | | */ |
612 | | static BOOL |
613 | 0 | jpeg_read_xmp_profile(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
614 | | // marker identifying string for XMP (null terminated) |
615 | 0 | const char *xmp_signature = "http://ns.adobe.com/xap/1.0/"; |
616 | | // XMP signature is 29 bytes long |
617 | 0 | const size_t xmp_signature_size = strlen(xmp_signature) + 1; |
618 | |
|
619 | 0 | size_t length = datalen; |
620 | 0 | BYTE *profile = (BYTE*)dataptr; |
621 | |
|
622 | 0 | if(length <= xmp_signature_size) { |
623 | | // avoid reading corrupted or empty data |
624 | 0 | return FALSE; |
625 | 0 | } |
626 | | |
627 | | // verify the identifying string |
628 | | |
629 | 0 | if(memcmp(xmp_signature, profile, strlen(xmp_signature)) == 0) { |
630 | | // XMP profile |
631 | |
|
632 | 0 | profile += xmp_signature_size; |
633 | 0 | length -= xmp_signature_size; |
634 | | |
635 | | // create a tag |
636 | 0 | FITAG *tag = FreeImage_CreateTag(); |
637 | 0 | if(tag) { |
638 | 0 | FreeImage_SetTagID(tag, JPEG_APP0+1); // 0xFFE1 |
639 | 0 | FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); |
640 | 0 | FreeImage_SetTagLength(tag, (DWORD)length); |
641 | 0 | FreeImage_SetTagCount(tag, (DWORD)length); |
642 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
643 | 0 | FreeImage_SetTagValue(tag, profile); |
644 | | |
645 | | // store the tag |
646 | 0 | FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); |
647 | | |
648 | | // destroy the tag |
649 | 0 | FreeImage_DeleteTag(tag); |
650 | 0 | } |
651 | |
|
652 | 0 | return TRUE; |
653 | 0 | } |
654 | | |
655 | 0 | return FALSE; |
656 | 0 | } |
657 | | |
658 | | /** |
659 | | Read JFIF "JFXX" extension APP0 marker |
660 | | @param dib Input FIBITMAP |
661 | | @param dataptr Pointer to the APP0 marker |
662 | | @param datalen APP0 marker length |
663 | | @return Returns TRUE if successful, FALSE otherwise |
664 | | */ |
665 | | static BOOL |
666 | 0 | jpeg_read_jfxx(FIBITMAP *dib, const BYTE *dataptr, unsigned int datalen) { |
667 | 0 | if(datalen < 6) { |
668 | 0 | return FALSE; |
669 | 0 | } |
670 | | |
671 | 0 | const int id_length = 5; |
672 | 0 | const BYTE *data = dataptr + id_length; |
673 | 0 | unsigned remaining = datalen - id_length; |
674 | | |
675 | 0 | const BYTE type = *data; |
676 | 0 | ++data, --remaining; |
677 | |
|
678 | 0 | switch(type) { |
679 | 0 | case JFXX_TYPE_JPEG: |
680 | 0 | { |
681 | | // load the thumbnail |
682 | 0 | FIMEMORY* hmem = FreeImage_OpenMemory(const_cast<BYTE*>(data), remaining); |
683 | 0 | FIBITMAP* thumbnail = FreeImage_LoadFromMemory(FIF_JPEG, hmem); |
684 | 0 | FreeImage_CloseMemory(hmem); |
685 | | // store the thumbnail |
686 | 0 | FreeImage_SetThumbnail(dib, thumbnail); |
687 | | // then delete it |
688 | 0 | FreeImage_Unload(thumbnail); |
689 | 0 | break; |
690 | 0 | } |
691 | 0 | case JFXX_TYPE_8bit: |
692 | | // colormapped uncompressed thumbnail (no supported) |
693 | 0 | break; |
694 | 0 | case JFXX_TYPE_24bit: |
695 | | // truecolor uncompressed thumbnail (no supported) |
696 | 0 | break; |
697 | 0 | default: |
698 | 0 | break; |
699 | 0 | } |
700 | | |
701 | 0 | return TRUE; |
702 | 0 | } |
703 | | |
704 | | |
705 | | /** |
706 | | Read JPEG special markers |
707 | | */ |
708 | | static BOOL |
709 | 0 | read_markers(j_decompress_ptr cinfo, FIBITMAP *dib) { |
710 | 0 | jpeg_saved_marker_ptr marker; |
711 | |
|
712 | 0 | for(marker = cinfo->marker_list; marker != NULL; marker = marker->next) { |
713 | 0 | switch(marker->marker) { |
714 | 0 | case JPEG_APP0: |
715 | | // JFIF is handled by libjpeg already, handle JFXX |
716 | 0 | if(memcmp(marker->data, "JFIF" , 5) == 0) { |
717 | 0 | continue; |
718 | 0 | } |
719 | 0 | if(memcmp(marker->data, "JFXX" , 5) == 0) { |
720 | 0 | if(!cinfo->saw_JFIF_marker || cinfo->JFIF_minor_version < 2) { |
721 | 0 | FreeImage_OutputMessageProc(s_format_id, "Warning: non-standard JFXX segment"); |
722 | 0 | } |
723 | 0 | jpeg_read_jfxx(dib, marker->data, marker->data_length); |
724 | 0 | } |
725 | | // other values such as 'Picasa' : ignore safely unknown APP0 marker |
726 | 0 | break; |
727 | 0 | case JPEG_COM: |
728 | | // JPEG comment |
729 | 0 | jpeg_read_comment(dib, marker->data, marker->data_length); |
730 | 0 | break; |
731 | 0 | case EXIF_MARKER: |
732 | | // Exif or Adobe XMP profile |
733 | 0 | jpeg_read_exif_profile(dib, marker->data, marker->data_length); |
734 | 0 | jpeg_read_xmp_profile(dib, marker->data, marker->data_length); |
735 | 0 | jpeg_read_exif_profile_raw(dib, marker->data, marker->data_length); |
736 | 0 | break; |
737 | 0 | case IPTC_MARKER: |
738 | | // IPTC/NAA or Adobe Photoshop profile |
739 | 0 | jpeg_read_iptc_profile(dib, marker->data, marker->data_length); |
740 | 0 | break; |
741 | 0 | } |
742 | 0 | } |
743 | | |
744 | | // ICC profile |
745 | 0 | BYTE *icc_profile = NULL; |
746 | 0 | unsigned icc_length = 0; |
747 | |
|
748 | 0 | if( jpeg_read_icc_profile(cinfo, &icc_profile, &icc_length) ) { |
749 | | // copy ICC profile data |
750 | 0 | FreeImage_CreateICCProfile(dib, icc_profile, icc_length); |
751 | | // clean up |
752 | 0 | free(icc_profile); |
753 | 0 | } |
754 | |
|
755 | 0 | return TRUE; |
756 | 0 | } |
757 | | |
758 | | // ---------------------------------------------------------- |
759 | | // Special markers write functions |
760 | | // ---------------------------------------------------------- |
761 | | |
762 | | /** |
763 | | Write JPEG_COM marker (comment) |
764 | | */ |
765 | | static BOOL |
766 | 0 | jpeg_write_comment(j_compress_ptr cinfo, FIBITMAP *dib) { |
767 | 0 | FITAG *tag = NULL; |
768 | | |
769 | | // write user comment as a JPEG_COM marker |
770 | 0 | FreeImage_GetMetadata(FIMD_COMMENTS, dib, "Comment", &tag); |
771 | 0 | if(tag) { |
772 | 0 | const char *tag_value = (char*)FreeImage_GetTagValue(tag); |
773 | |
|
774 | 0 | if(NULL != tag_value) { |
775 | 0 | for(long i = 0; i < (long)strlen(tag_value); i+= MAX_BYTES_IN_MARKER) { |
776 | 0 | jpeg_write_marker(cinfo, JPEG_COM, (BYTE*)tag_value + i, MIN((long)strlen(tag_value + i), MAX_BYTES_IN_MARKER)); |
777 | 0 | } |
778 | 0 | return TRUE; |
779 | 0 | } |
780 | 0 | } |
781 | 0 | return FALSE; |
782 | 0 | } |
783 | | |
784 | | /** |
785 | | Write JPEG_APP2 marker (ICC profile) |
786 | | */ |
787 | | static BOOL |
788 | 0 | jpeg_write_icc_profile(j_compress_ptr cinfo, FIBITMAP *dib) { |
789 | | // marker identifying string "ICC_PROFILE" (null-terminated) |
790 | 0 | BYTE icc_signature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00 }; |
791 | |
|
792 | 0 | FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); |
793 | |
|
794 | 0 | if (iccProfile->size && iccProfile->data) { |
795 | | // ICC_HEADER_SIZE: ICC signature is 'ICC_PROFILE' + 2 bytes |
796 | |
|
797 | 0 | BYTE *profile = (BYTE*)malloc((iccProfile->size + ICC_HEADER_SIZE) * sizeof(BYTE)); |
798 | 0 | if(profile == NULL) return FALSE; |
799 | 0 | memcpy(profile, icc_signature, 12); |
800 | |
|
801 | 0 | for(long i = 0; i < (long)iccProfile->size; i += MAX_DATA_BYTES_IN_MARKER) { |
802 | 0 | unsigned length = MIN((long)(iccProfile->size - i), MAX_DATA_BYTES_IN_MARKER); |
803 | | // sequence number |
804 | 0 | profile[12] = (BYTE) ((i / MAX_DATA_BYTES_IN_MARKER) + 1); |
805 | | // number of markers |
806 | 0 | profile[13] = (BYTE) (iccProfile->size / MAX_DATA_BYTES_IN_MARKER + 1); |
807 | |
|
808 | 0 | memcpy(profile + ICC_HEADER_SIZE, (BYTE*)iccProfile->data + i, length); |
809 | 0 | jpeg_write_marker(cinfo, ICC_MARKER, profile, (length + ICC_HEADER_SIZE)); |
810 | 0 | } |
811 | |
|
812 | 0 | free(profile); |
813 | |
|
814 | 0 | return TRUE; |
815 | 0 | } |
816 | | |
817 | 0 | return FALSE; |
818 | 0 | } |
819 | | |
820 | | /** |
821 | | Write JPEG_APPD marker (IPTC or Adobe Photoshop profile) |
822 | | @return Returns TRUE if successful, FALSE otherwise |
823 | | */ |
824 | | static BOOL |
825 | 0 | jpeg_write_iptc_profile(j_compress_ptr cinfo, FIBITMAP *dib) { |
826 | | //const char *ps_header = "Photoshop 3.0\x08BIM\x04\x04\x0\x0\x0\x0"; |
827 | 0 | const unsigned tag_length = 26; |
828 | |
|
829 | 0 | if(FreeImage_GetMetadataCount(FIMD_IPTC, dib)) { |
830 | 0 | BYTE *profile = NULL; |
831 | 0 | unsigned profile_size = 0; |
832 | | |
833 | | // create a binary profile |
834 | 0 | if(write_iptc_profile(dib, &profile, &profile_size)) { |
835 | | |
836 | | // write the profile |
837 | 0 | for(long i = 0; i < (long)profile_size; i += 65517L) { |
838 | 0 | unsigned length = MIN((long)profile_size - i, 65517L); |
839 | 0 | unsigned roundup = length & 0x01; // needed for Photoshop |
840 | 0 | BYTE *iptc_profile = (BYTE*)malloc(length + roundup + tag_length); |
841 | 0 | if(iptc_profile == NULL) break; |
842 | | // Photoshop identification string |
843 | 0 | memcpy(&iptc_profile[0], "Photoshop 3.0\x0", 14); |
844 | | // 8BIM segment type |
845 | 0 | memcpy(&iptc_profile[14], "8BIM\x04\x04\x0\x0\x0\x0", 10); |
846 | | // segment size |
847 | 0 | iptc_profile[24] = (BYTE)(length >> 8); |
848 | 0 | iptc_profile[25] = (BYTE)(length & 0xFF); |
849 | | // segment data |
850 | 0 | memcpy(&iptc_profile[tag_length], &profile[i], length); |
851 | 0 | if(roundup) |
852 | 0 | iptc_profile[length + tag_length] = 0; |
853 | 0 | jpeg_write_marker(cinfo, IPTC_MARKER, iptc_profile, length + roundup + tag_length); |
854 | 0 | free(iptc_profile); |
855 | 0 | } |
856 | | |
857 | | // release profile |
858 | 0 | free(profile); |
859 | |
|
860 | 0 | return TRUE; |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | 0 | return FALSE; |
865 | 0 | } |
866 | | |
867 | | /** |
868 | | Write JPEG_APP1 marker (XMP profile) |
869 | | @return Returns TRUE if successful, FALSE otherwise |
870 | | */ |
871 | | static BOOL |
872 | 0 | jpeg_write_xmp_profile(j_compress_ptr cinfo, FIBITMAP *dib) { |
873 | | // marker identifying string for XMP (null terminated) |
874 | 0 | const char *xmp_signature = "http://ns.adobe.com/xap/1.0/"; |
875 | |
|
876 | 0 | FITAG *tag_xmp = NULL; |
877 | 0 | FreeImage_GetMetadata(FIMD_XMP, dib, g_TagLib_XMPFieldName, &tag_xmp); |
878 | |
|
879 | 0 | if(tag_xmp) { |
880 | 0 | const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_xmp); |
881 | |
|
882 | 0 | if(NULL != tag_value) { |
883 | | // XMP signature is 29 bytes long |
884 | 0 | unsigned int xmp_header_size = (unsigned int)strlen(xmp_signature) + 1; |
885 | |
|
886 | 0 | DWORD tag_length = FreeImage_GetTagLength(tag_xmp); |
887 | |
|
888 | 0 | BYTE *profile = (BYTE*)malloc((tag_length + xmp_header_size) * sizeof(BYTE)); |
889 | 0 | if(profile == NULL) return FALSE; |
890 | 0 | memcpy(profile, xmp_signature, xmp_header_size); |
891 | |
|
892 | 0 | for(DWORD i = 0; i < tag_length; i += 65504L) { |
893 | 0 | unsigned length = MIN((long)(tag_length - i), 65504L); |
894 | | |
895 | 0 | memcpy(profile + xmp_header_size, tag_value + i, length); |
896 | 0 | jpeg_write_marker(cinfo, EXIF_MARKER, profile, (length + xmp_header_size)); |
897 | 0 | } |
898 | |
|
899 | 0 | free(profile); |
900 | |
|
901 | 0 | return TRUE; |
902 | 0 | } |
903 | 0 | } |
904 | | |
905 | 0 | return FALSE; |
906 | 0 | } |
907 | | |
908 | | /** |
909 | | Write JPEG_APP1 marker (Exif profile) |
910 | | @return Returns TRUE if successful, FALSE otherwise |
911 | | */ |
912 | | static BOOL |
913 | 0 | jpeg_write_exif_profile_raw(j_compress_ptr cinfo, FIBITMAP *dib) { |
914 | | // marker identifying string for Exif = "Exif\0\0" |
915 | 0 | BYTE exif_signature[6] = { 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 }; |
916 | |
|
917 | 0 | FITAG *tag_exif = NULL; |
918 | 0 | FreeImage_GetMetadata(FIMD_EXIF_RAW, dib, g_TagLib_ExifRawFieldName, &tag_exif); |
919 | |
|
920 | 0 | if(tag_exif) { |
921 | 0 | const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag_exif); |
922 | | |
923 | | // verify the identifying string |
924 | 0 | if(memcmp(exif_signature, tag_value, sizeof(exif_signature)) != 0) { |
925 | | // not an Exif profile |
926 | 0 | return FALSE; |
927 | 0 | } |
928 | | |
929 | 0 | if(NULL != tag_value) { |
930 | 0 | DWORD tag_length = FreeImage_GetTagLength(tag_exif); |
931 | |
|
932 | 0 | BYTE *profile = (BYTE*)malloc(tag_length * sizeof(BYTE)); |
933 | 0 | if(profile == NULL) return FALSE; |
934 | | |
935 | 0 | for(DWORD i = 0; i < tag_length; i += 65504L) { |
936 | 0 | unsigned length = MIN((long)(tag_length - i), 65504L); |
937 | | |
938 | 0 | memcpy(profile, tag_value + i, length); |
939 | 0 | jpeg_write_marker(cinfo, EXIF_MARKER, profile, length); |
940 | 0 | } |
941 | |
|
942 | 0 | free(profile); |
943 | |
|
944 | 0 | return TRUE; |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | 0 | return FALSE; |
949 | 0 | } |
950 | | |
951 | | /** |
952 | | Write thumbnail (JFXX segment, JPEG compressed) |
953 | | */ |
954 | | static BOOL |
955 | 0 | jpeg_write_jfxx(j_compress_ptr cinfo, FIBITMAP *dib) { |
956 | | // get the thumbnail to be stored |
957 | 0 | FIBITMAP* thumbnail = FreeImage_GetThumbnail(dib); |
958 | 0 | if(!thumbnail) { |
959 | 0 | return TRUE; |
960 | 0 | } |
961 | | // check for a compatible output format |
962 | 0 | if((FreeImage_GetImageType(thumbnail) != FIT_BITMAP) || (FreeImage_GetBPP(thumbnail) != 8) && (FreeImage_GetBPP(thumbnail) != 24)) { |
963 | 0 | FreeImage_OutputMessageProc(s_format_id, FI_MSG_WARNING_INVALID_THUMBNAIL); |
964 | 0 | return FALSE; |
965 | 0 | } |
966 | | |
967 | | // stores the thumbnail as a baseline JPEG into a memory block |
968 | | // return the memory block only if its size is within JFXX marker size limit! |
969 | 0 | FIMEMORY *stream = FreeImage_OpenMemory(); |
970 | | |
971 | 0 | if(FreeImage_SaveToMemory(FIF_JPEG, thumbnail, stream, JPEG_BASELINE)) { |
972 | | // check that the memory block size is within JFXX marker size limit |
973 | 0 | FreeImage_SeekMemory(stream, 0, SEEK_END); |
974 | 0 | const long eof = FreeImage_TellMemory(stream); |
975 | 0 | if(eof > MAX_JFXX_THUMB_SIZE) { |
976 | 0 | FreeImage_OutputMessageProc(s_format_id, "Warning: attached thumbnail is %d bytes larger than maximum supported size - Thumbnail saving aborted", eof - MAX_JFXX_THUMB_SIZE); |
977 | 0 | FreeImage_CloseMemory(stream); |
978 | 0 | return FALSE; |
979 | 0 | } |
980 | 0 | } else { |
981 | 0 | FreeImage_CloseMemory(stream); |
982 | 0 | return FALSE; |
983 | 0 | } |
984 | | |
985 | 0 | BYTE* thData = NULL; |
986 | 0 | DWORD thSize = 0; |
987 | | |
988 | 0 | FreeImage_AcquireMemory(stream, &thData, &thSize); |
989 | | |
990 | 0 | BYTE id_length = 5; //< "JFXX" |
991 | 0 | BYTE type = JFXX_TYPE_JPEG; |
992 | | |
993 | 0 | DWORD totalsize = id_length + sizeof(type) + thSize; |
994 | 0 | jpeg_write_m_header(cinfo, JPEG_APP0, totalsize); |
995 | | |
996 | 0 | jpeg_write_m_byte(cinfo, 'J'); |
997 | 0 | jpeg_write_m_byte(cinfo, 'F'); |
998 | 0 | jpeg_write_m_byte(cinfo, 'X'); |
999 | 0 | jpeg_write_m_byte(cinfo, 'X'); |
1000 | 0 | jpeg_write_m_byte(cinfo, '\0'); |
1001 | | |
1002 | 0 | jpeg_write_m_byte(cinfo, type); |
1003 | | |
1004 | | // write thumbnail to destination. |
1005 | | // We "cram it straight into the data destination module", because write_m_byte is slow |
1006 | | |
1007 | 0 | freeimage_dst_ptr dest = (freeimage_dst_ptr) cinfo->dest; |
1008 | | |
1009 | 0 | BYTE* & out = dest->pub.next_output_byte; |
1010 | 0 | size_t & bufRemain = dest->pub.free_in_buffer; |
1011 | | |
1012 | 0 | const BYTE *thData_end = thData + thSize; |
1013 | |
|
1014 | 0 | while(thData < thData_end) { |
1015 | 0 | *(out)++ = *(thData)++; |
1016 | 0 | if (--bufRemain == 0) { |
1017 | | // buffer full - flush |
1018 | 0 | if (!dest->pub.empty_output_buffer(cinfo)) { |
1019 | 0 | break; |
1020 | 0 | } |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | 0 | FreeImage_CloseMemory(stream); |
1025 | |
|
1026 | 0 | return TRUE; |
1027 | 0 | } |
1028 | | |
1029 | | /** |
1030 | | Write JPEG special markers |
1031 | | */ |
1032 | | static BOOL |
1033 | 0 | write_markers(j_compress_ptr cinfo, FIBITMAP *dib) { |
1034 | | // write thumbnail as a JFXX marker |
1035 | 0 | jpeg_write_jfxx(cinfo, dib); |
1036 | | |
1037 | | // write user comment as a JPEG_COM marker |
1038 | 0 | jpeg_write_comment(cinfo, dib); |
1039 | | |
1040 | | // write ICC profile |
1041 | 0 | jpeg_write_icc_profile(cinfo, dib); |
1042 | | |
1043 | | // write IPTC profile |
1044 | 0 | jpeg_write_iptc_profile(cinfo, dib); |
1045 | | |
1046 | | // write Adobe XMP profile |
1047 | 0 | jpeg_write_xmp_profile(cinfo, dib); |
1048 | | |
1049 | | // write Exif raw data |
1050 | 0 | jpeg_write_exif_profile_raw(cinfo, dib); |
1051 | |
|
1052 | 0 | return TRUE; |
1053 | 0 | } |
1054 | | |
1055 | | // ------------------------------------------------------------ |
1056 | | // Keep original size info when using scale option on loading |
1057 | | // ------------------------------------------------------------ |
1058 | | static void |
1059 | 0 | store_size_info(FIBITMAP *dib, JDIMENSION width, JDIMENSION height) { |
1060 | 0 | char buffer[256]; |
1061 | | // create a tag |
1062 | 0 | FITAG *tag = FreeImage_CreateTag(); |
1063 | 0 | if(tag) { |
1064 | 0 | size_t length = 0; |
1065 | | // set the original width |
1066 | 0 | sprintf(buffer, "%d", (int)width); |
1067 | 0 | length = strlen(buffer) + 1; // include the NULL/0 value |
1068 | 0 | FreeImage_SetTagKey(tag, "OriginalJPEGWidth"); |
1069 | 0 | FreeImage_SetTagLength(tag, (DWORD)length); |
1070 | 0 | FreeImage_SetTagCount(tag, (DWORD)length); |
1071 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
1072 | 0 | FreeImage_SetTagValue(tag, buffer); |
1073 | 0 | FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); |
1074 | | // set the original height |
1075 | 0 | sprintf(buffer, "%d", (int)height); |
1076 | 0 | length = strlen(buffer) + 1; // include the NULL/0 value |
1077 | 0 | FreeImage_SetTagKey(tag, "OriginalJPEGHeight"); |
1078 | 0 | FreeImage_SetTagLength(tag, (DWORD)length); |
1079 | 0 | FreeImage_SetTagCount(tag, (DWORD)length); |
1080 | 0 | FreeImage_SetTagType(tag, FIDT_ASCII); |
1081 | 0 | FreeImage_SetTagValue(tag, buffer); |
1082 | 0 | FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); |
1083 | | // destroy the tag |
1084 | 0 | FreeImage_DeleteTag(tag); |
1085 | 0 | } |
1086 | 0 | } |
1087 | | |
1088 | | // ========================================================== |
1089 | | // Plugin Implementation |
1090 | | // ========================================================== |
1091 | | |
1092 | | static const char * DLL_CALLCONV |
1093 | 2 | Format() { |
1094 | 2 | return "JPEG"; |
1095 | 2 | } |
1096 | | |
1097 | | static const char * DLL_CALLCONV |
1098 | 0 | Description() { |
1099 | 0 | return "JPEG - JFIF Compliant"; |
1100 | 0 | } |
1101 | | |
1102 | | static const char * DLL_CALLCONV |
1103 | 0 | Extension() { |
1104 | 0 | return "jpg,jif,jpeg,jpe"; |
1105 | 0 | } |
1106 | | |
1107 | | static const char * DLL_CALLCONV |
1108 | 0 | RegExpr() { |
1109 | 0 | return "^\377\330\377"; |
1110 | 0 | } |
1111 | | |
1112 | | static const char * DLL_CALLCONV |
1113 | 0 | MimeType() { |
1114 | 0 | return "image/jpeg"; |
1115 | 0 | } |
1116 | | |
1117 | | static BOOL DLL_CALLCONV |
1118 | 24.4k | Validate(FreeImageIO *io, fi_handle handle) { |
1119 | 24.4k | BYTE jpeg_signature[] = { 0xFF, 0xD8 }; |
1120 | 24.4k | BYTE signature[2] = { 0, 0 }; |
1121 | | |
1122 | 24.4k | io->read_proc(signature, 1, sizeof(jpeg_signature), handle); |
1123 | | |
1124 | 24.4k | return (memcmp(jpeg_signature, signature, sizeof(jpeg_signature)) == 0); |
1125 | 24.4k | } |
1126 | | |
1127 | | static BOOL DLL_CALLCONV |
1128 | 0 | SupportsExportDepth(int depth) { |
1129 | 0 | return ( |
1130 | 0 | (depth == 8) || |
1131 | 0 | (depth == 24) || |
1132 | 0 | (depth == 32) // only if 32-bit CMYK |
1133 | 0 | ); |
1134 | 0 | } |
1135 | | |
1136 | | static BOOL DLL_CALLCONV |
1137 | 0 | SupportsExportType(FREE_IMAGE_TYPE type) { |
1138 | 0 | return (type == FIT_BITMAP) ? TRUE : FALSE; |
1139 | 0 | } |
1140 | | |
1141 | | static BOOL DLL_CALLCONV |
1142 | 0 | SupportsICCProfiles() { |
1143 | 0 | return TRUE; |
1144 | 0 | } |
1145 | | |
1146 | | static BOOL DLL_CALLCONV |
1147 | 0 | SupportsNoPixels() { |
1148 | 0 | return TRUE; |
1149 | 0 | } |
1150 | | |
1151 | | // ---------------------------------------------------------- |
1152 | | |
1153 | | static FIBITMAP * DLL_CALLCONV |
1154 | 0 | Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { |
1155 | 0 | if (handle) { |
1156 | 0 | FIBITMAP *dib = NULL; |
1157 | |
|
1158 | 0 | BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; |
1159 | | |
1160 | | // set up the jpeglib structures |
1161 | |
|
1162 | 0 | struct jpeg_decompress_struct cinfo; |
1163 | 0 | ErrorManager fi_error_mgr; |
1164 | |
|
1165 | 0 | try { |
1166 | | |
1167 | | // step 1: allocate and initialize JPEG decompression object |
1168 | | |
1169 | | // we set up the normal JPEG error routines, then override error_exit & output_message |
1170 | 0 | cinfo.err = jpeg_std_error(&fi_error_mgr.pub); |
1171 | 0 | fi_error_mgr.pub.error_exit = jpeg_error_exit; |
1172 | 0 | fi_error_mgr.pub.output_message = jpeg_output_message; |
1173 | | |
1174 | | // establish the setjmp return context for jpeg_error_exit to use |
1175 | 0 | if (setjmp(fi_error_mgr.setjmp_buffer)) { |
1176 | | // If we get here, the JPEG code has signaled an error. |
1177 | | // We need to clean up the JPEG object, close the input file, and return. |
1178 | 0 | jpeg_destroy_decompress(&cinfo); |
1179 | 0 | throw (const char*)NULL; |
1180 | 0 | } |
1181 | | |
1182 | 0 | jpeg_create_decompress(&cinfo); |
1183 | | |
1184 | | // step 2a: specify data source (eg, a handle) |
1185 | |
|
1186 | 0 | jpeg_freeimage_src(&cinfo, handle, io); |
1187 | | |
1188 | | // step 2b: save special markers for later reading |
1189 | | |
1190 | 0 | jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF); |
1191 | 0 | for(int m = 0; m < 16; m++) { |
1192 | 0 | jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); |
1193 | 0 | } |
1194 | | |
1195 | | // step 3: read handle parameters with jpeg_read_header() |
1196 | |
|
1197 | 0 | jpeg_read_header(&cinfo, TRUE); |
1198 | | |
1199 | | // step 4: set parameters for decompression |
1200 | |
|
1201 | 0 | unsigned int scale_denom = 1; // fraction by which to scale image |
1202 | 0 | int requested_size = flags >> 16; // requested user size in pixels |
1203 | 0 | if(requested_size > 0) { |
1204 | | // the JPEG codec can perform x2, x4 or x8 scaling on loading |
1205 | | // try to find the more appropriate scaling according to user's need |
1206 | 0 | double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size; |
1207 | 0 | if(scale >= 8) { |
1208 | 0 | scale_denom = 8; |
1209 | 0 | } else if(scale >= 4) { |
1210 | 0 | scale_denom = 4; |
1211 | 0 | } else if(scale >= 2) { |
1212 | 0 | scale_denom = 2; |
1213 | 0 | } |
1214 | 0 | } |
1215 | 0 | cinfo.scale_num = 1; |
1216 | 0 | cinfo.scale_denom = scale_denom; |
1217 | |
|
1218 | 0 | if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) { |
1219 | 0 | cinfo.dct_method = JDCT_IFAST; |
1220 | 0 | cinfo.do_fancy_upsampling = FALSE; |
1221 | 0 | } |
1222 | |
|
1223 | 0 | if ((flags & JPEG_GREYSCALE) == JPEG_GREYSCALE) { |
1224 | | // force loading as a 8-bit greyscale image |
1225 | 0 | cinfo.out_color_space = JCS_GRAYSCALE; |
1226 | 0 | } |
1227 | | |
1228 | | // step 5a: start decompressor and calculate output width and height |
1229 | |
|
1230 | 0 | jpeg_start_decompress(&cinfo); |
1231 | | |
1232 | | // step 5b: allocate dib and init header |
1233 | |
|
1234 | 0 | if((cinfo.output_components == 4) && (cinfo.out_color_space == JCS_CMYK)) { |
1235 | | // CMYK image |
1236 | 0 | if((flags & JPEG_CMYK) == JPEG_CMYK) { |
1237 | | // load as CMYK |
1238 | 0 | dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
1239 | 0 | if(!dib) throw FI_MSG_ERROR_DIB_MEMORY; |
1240 | 0 | FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK; |
1241 | 0 | } else { |
1242 | | // load as CMYK and convert to RGB |
1243 | 0 | dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
1244 | 0 | if(!dib) throw FI_MSG_ERROR_DIB_MEMORY; |
1245 | 0 | } |
1246 | 0 | } else { |
1247 | | // RGB or greyscale image |
1248 | 0 | dib = FreeImage_AllocateHeader(header_only, cinfo.output_width, cinfo.output_height, 8 * cinfo.output_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); |
1249 | 0 | if(!dib) throw FI_MSG_ERROR_DIB_MEMORY; |
1250 | | |
1251 | 0 | if (cinfo.output_components == 1) { |
1252 | | // build a greyscale palette |
1253 | 0 | RGBQUAD *colors = FreeImage_GetPalette(dib); |
1254 | |
|
1255 | 0 | for (int i = 0; i < 256; i++) { |
1256 | 0 | colors[i].rgbRed = (BYTE)i; |
1257 | 0 | colors[i].rgbGreen = (BYTE)i; |
1258 | 0 | colors[i].rgbBlue = (BYTE)i; |
1259 | 0 | } |
1260 | 0 | } |
1261 | 0 | } |
1262 | 0 | if(scale_denom != 1) { |
1263 | | // store original size info if a scaling was requested |
1264 | 0 | store_size_info(dib, cinfo.image_width, cinfo.image_height); |
1265 | 0 | } |
1266 | | |
1267 | | // step 5c: handle metrices |
1268 | |
|
1269 | 0 | if (cinfo.density_unit == 1) { |
1270 | | // dots/inch |
1271 | 0 | FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5)); |
1272 | 0 | FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5)); |
1273 | 0 | } else if (cinfo.density_unit == 2) { |
1274 | | // dots/cm |
1275 | 0 | FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100)); |
1276 | 0 | FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100)); |
1277 | 0 | } |
1278 | | |
1279 | | // step 6: read special markers |
1280 | | |
1281 | 0 | read_markers(&cinfo, dib); |
1282 | | |
1283 | | // --- header only mode => clean-up and return |
1284 | |
|
1285 | 0 | if (header_only) { |
1286 | | // release JPEG decompression object |
1287 | 0 | jpeg_destroy_decompress(&cinfo); |
1288 | | // return header data |
1289 | 0 | return dib; |
1290 | 0 | } |
1291 | | |
1292 | | // step 7a: while (scan lines remain to be read) jpeg_read_scanlines(...); |
1293 | | |
1294 | 0 | if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) { |
1295 | | // convert from CMYK to RGB |
1296 | |
|
1297 | 0 | JSAMPARRAY buffer; // output row buffer |
1298 | 0 | unsigned row_stride; // physical row width in output buffer |
1299 | | |
1300 | | // JSAMPLEs per row in output buffer |
1301 | 0 | row_stride = cinfo.output_width * cinfo.output_components; |
1302 | | // make a one-row-high sample array that will go away when done with image |
1303 | 0 | buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); |
1304 | |
|
1305 | 0 | while (cinfo.output_scanline < cinfo.output_height) { |
1306 | 0 | JSAMPROW src = buffer[0]; |
1307 | 0 | JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); |
1308 | |
|
1309 | 0 | jpeg_read_scanlines(&cinfo, buffer, 1); |
1310 | |
|
1311 | 0 | for(unsigned x = 0; x < cinfo.output_width; x++) { |
1312 | 0 | WORD K = (WORD)src[3]; |
1313 | 0 | dst[FI_RGBA_RED] = (BYTE)((K * src[0]) / 255); // C -> R |
1314 | 0 | dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255); // M -> G |
1315 | 0 | dst[FI_RGBA_BLUE] = (BYTE)((K * src[2]) / 255); // Y -> B |
1316 | 0 | src += 4; |
1317 | 0 | dst += 3; |
1318 | 0 | } |
1319 | 0 | } |
1320 | | |
1321 | | // if original image is CMYK but is converted to RGB, remove ICC profile from Exif-TIFF metadata |
1322 | 0 | FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, "InterColorProfile", NULL); |
1323 | |
|
1324 | 0 | } else if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) == JPEG_CMYK)) { |
1325 | | // convert from LibJPEG CMYK to standard CMYK |
1326 | |
|
1327 | 0 | JSAMPARRAY buffer; // output row buffer |
1328 | 0 | unsigned row_stride; // physical row width in output buffer |
1329 | | |
1330 | | // JSAMPLEs per row in output buffer |
1331 | 0 | row_stride = cinfo.output_width * cinfo.output_components; |
1332 | | // make a one-row-high sample array that will go away when done with image |
1333 | 0 | buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); |
1334 | |
|
1335 | 0 | while (cinfo.output_scanline < cinfo.output_height) { |
1336 | 0 | JSAMPROW src = buffer[0]; |
1337 | 0 | JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); |
1338 | |
|
1339 | 0 | jpeg_read_scanlines(&cinfo, buffer, 1); |
1340 | |
|
1341 | 0 | for(unsigned x = 0; x < cinfo.output_width; x++) { |
1342 | | // CMYK pixels are inverted |
1343 | 0 | dst[0] = ~src[0]; // C |
1344 | 0 | dst[1] = ~src[1]; // M |
1345 | 0 | dst[2] = ~src[2]; // Y |
1346 | 0 | dst[3] = ~src[3]; // K |
1347 | 0 | src += 4; |
1348 | 0 | dst += 4; |
1349 | 0 | } |
1350 | 0 | } |
1351 | |
|
1352 | 0 | } else { |
1353 | | // normal case (RGB or greyscale image) |
1354 | |
|
1355 | 0 | while (cinfo.output_scanline < cinfo.output_height) { |
1356 | 0 | JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); |
1357 | |
|
1358 | 0 | jpeg_read_scanlines(&cinfo, &dst, 1); |
1359 | 0 | } |
1360 | | |
1361 | | // step 7b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...) |
1362 | | // The default behavior of the JPEG library is kept "as is" because LibTIFF uses |
1363 | | // LibJPEG "as is". |
1364 | |
|
1365 | 0 | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR |
1366 | 0 | SwapRedBlue32(dib); |
1367 | 0 | #endif |
1368 | 0 | } |
1369 | | |
1370 | | // step 8: finish decompression |
1371 | |
|
1372 | 0 | jpeg_finish_decompress(&cinfo); |
1373 | | |
1374 | | // step 9: release JPEG decompression object |
1375 | |
|
1376 | 0 | jpeg_destroy_decompress(&cinfo); |
1377 | | |
1378 | | // check for automatic Exif rotation |
1379 | 0 | if(!header_only && ((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE)) { |
1380 | 0 | RotateExif(&dib); |
1381 | 0 | } |
1382 | | |
1383 | | // everything went well. return the loaded dib |
1384 | |
|
1385 | 0 | return dib; |
1386 | |
|
1387 | 0 | } catch (const char *text) { |
1388 | 0 | jpeg_destroy_decompress(&cinfo); |
1389 | 0 | if(NULL != dib) { |
1390 | 0 | FreeImage_Unload(dib); |
1391 | 0 | } |
1392 | 0 | if(NULL != text) { |
1393 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
1394 | 0 | } |
1395 | 0 | } |
1396 | 0 | } |
1397 | | |
1398 | 0 | return NULL; |
1399 | 0 | } |
1400 | | |
1401 | | // ---------------------------------------------------------- |
1402 | | |
1403 | | static BOOL DLL_CALLCONV |
1404 | 0 | Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { |
1405 | 0 | if ((dib) && (handle)) { |
1406 | 0 | try { |
1407 | | // Check dib format |
1408 | |
|
1409 | 0 | const char *sError = "only 24-bit RGB, 8-bit greyscale/palette or 32-bit CMYK bitmaps can be saved as JPEG"; |
1410 | |
|
1411 | 0 | FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); |
1412 | 0 | WORD bpp = (WORD)FreeImage_GetBPP(dib); |
1413 | |
|
1414 | 0 | if ((bpp != 24) && (bpp != 8) && !(bpp == 32 && (color_type == FIC_CMYK))) { |
1415 | 0 | throw sError; |
1416 | 0 | } |
1417 | | |
1418 | 0 | if(bpp == 8) { |
1419 | | // allow grey, reverse grey and palette |
1420 | 0 | if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) { |
1421 | 0 | throw sError; |
1422 | 0 | } |
1423 | 0 | } |
1424 | | |
1425 | | |
1426 | 0 | struct jpeg_compress_struct cinfo; |
1427 | 0 | ErrorManager fi_error_mgr; |
1428 | | |
1429 | | // Step 1: allocate and initialize JPEG compression object |
1430 | | |
1431 | | // we set up the normal JPEG error routines, then override error_exit & output_message |
1432 | 0 | cinfo.err = jpeg_std_error(&fi_error_mgr.pub); |
1433 | 0 | fi_error_mgr.pub.error_exit = jpeg_error_exit; |
1434 | 0 | fi_error_mgr.pub.output_message = jpeg_output_message; |
1435 | | |
1436 | | // establish the setjmp return context for jpeg_error_exit to use |
1437 | 0 | if (setjmp(fi_error_mgr.setjmp_buffer)) { |
1438 | | // If we get here, the JPEG code has signaled an error. |
1439 | | // We need to clean up the JPEG object, close the input file, and return. |
1440 | 0 | jpeg_destroy_compress(&cinfo); |
1441 | 0 | throw (const char*)NULL; |
1442 | 0 | } |
1443 | | |
1444 | | // Now we can initialize the JPEG compression object |
1445 | | |
1446 | 0 | jpeg_create_compress(&cinfo); |
1447 | | |
1448 | | // Step 2: specify data destination (eg, a file) |
1449 | |
|
1450 | 0 | jpeg_freeimage_dst(&cinfo, handle, io); |
1451 | | |
1452 | | // Step 3: set parameters for compression |
1453 | |
|
1454 | 0 | cinfo.image_width = FreeImage_GetWidth(dib); |
1455 | 0 | cinfo.image_height = FreeImage_GetHeight(dib); |
1456 | |
|
1457 | 0 | switch(color_type) { |
1458 | 0 | case FIC_MINISBLACK : |
1459 | 0 | case FIC_MINISWHITE : |
1460 | 0 | cinfo.in_color_space = JCS_GRAYSCALE; |
1461 | 0 | cinfo.input_components = 1; |
1462 | 0 | break; |
1463 | 0 | case FIC_CMYK: |
1464 | 0 | cinfo.in_color_space = JCS_CMYK; |
1465 | 0 | cinfo.input_components = 4; |
1466 | 0 | break; |
1467 | 0 | default : |
1468 | 0 | cinfo.in_color_space = JCS_RGB; |
1469 | 0 | cinfo.input_components = 3; |
1470 | 0 | break; |
1471 | 0 | } |
1472 | | |
1473 | 0 | jpeg_set_defaults(&cinfo); |
1474 | | |
1475 | | // progressive-JPEG support |
1476 | 0 | if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) { |
1477 | 0 | jpeg_simple_progression(&cinfo); |
1478 | 0 | } |
1479 | | |
1480 | | // compute optimal Huffman coding tables for the image |
1481 | 0 | if((flags & JPEG_OPTIMIZE) == JPEG_OPTIMIZE) { |
1482 | 0 | cinfo.optimize_coding = TRUE; |
1483 | 0 | } |
1484 | | |
1485 | | // Set JFIF density parameters from the DIB data |
1486 | |
|
1487 | 0 | cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib)); |
1488 | 0 | cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib)); |
1489 | 0 | cinfo.density_unit = 1; // dots / inch |
1490 | | |
1491 | | // thumbnail support (JFIF 1.02 extension markers) |
1492 | 0 | if(FreeImage_GetThumbnail(dib) != NULL) { |
1493 | 0 | cinfo.write_JFIF_header = static_cast<boolean>(1); //<### force it, though when color is CMYK it will be incorrect |
1494 | 0 | cinfo.JFIF_minor_version = 2; |
1495 | 0 | } |
1496 | | |
1497 | | // baseline JPEG support |
1498 | 0 | if ((flags & JPEG_BASELINE) == JPEG_BASELINE) { |
1499 | 0 | cinfo.write_JFIF_header = static_cast<boolean>(0); // No marker for non-JFIF colorspaces |
1500 | 0 | cinfo.write_Adobe_marker = static_cast<boolean>(0); // write no Adobe marker by default |
1501 | 0 | } |
1502 | | |
1503 | | // set subsampling options if required |
1504 | |
|
1505 | 0 | if(cinfo.in_color_space == JCS_RGB) { |
1506 | 0 | if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { |
1507 | | // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100% |
1508 | | // the horizontal color resolution is quartered |
1509 | 0 | cinfo.comp_info[0].h_samp_factor = 4; // Y |
1510 | 0 | cinfo.comp_info[0].v_samp_factor = 1; |
1511 | 0 | cinfo.comp_info[1].h_samp_factor = 1; // Cb |
1512 | 0 | cinfo.comp_info[1].v_samp_factor = 1; |
1513 | 0 | cinfo.comp_info[2].h_samp_factor = 1; // Cr |
1514 | 0 | cinfo.comp_info[2].v_samp_factor = 1; |
1515 | 0 | } else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) { |
1516 | | // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50% |
1517 | | // the chrominance resolution in both the horizontal and vertical directions is cut in half |
1518 | 0 | cinfo.comp_info[0].h_samp_factor = 2; // Y |
1519 | 0 | cinfo.comp_info[0].v_samp_factor = 2; |
1520 | 0 | cinfo.comp_info[1].h_samp_factor = 1; // Cb |
1521 | 0 | cinfo.comp_info[1].v_samp_factor = 1; |
1522 | 0 | cinfo.comp_info[2].h_samp_factor = 1; // Cr |
1523 | 0 | cinfo.comp_info[2].v_samp_factor = 1; |
1524 | 0 | } else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) |
1525 | | // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100% |
1526 | | // half of the horizontal resolution in the chrominance is dropped (Cb & Cr), |
1527 | | // while the full resolution is retained in the vertical direction, with respect to the luminance |
1528 | 0 | cinfo.comp_info[0].h_samp_factor = 2; // Y |
1529 | 0 | cinfo.comp_info[0].v_samp_factor = 1; |
1530 | 0 | cinfo.comp_info[1].h_samp_factor = 1; // Cb |
1531 | 0 | cinfo.comp_info[1].v_samp_factor = 1; |
1532 | 0 | cinfo.comp_info[2].h_samp_factor = 1; // Cr |
1533 | 0 | cinfo.comp_info[2].v_samp_factor = 1; |
1534 | 0 | } |
1535 | 0 | else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) |
1536 | | // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100% |
1537 | | // the resolution of chrominance information (Cb & Cr) is preserved |
1538 | | // at the same rate as the luminance (Y) information |
1539 | 0 | cinfo.comp_info[0].h_samp_factor = 1; // Y |
1540 | 0 | cinfo.comp_info[0].v_samp_factor = 1; |
1541 | 0 | cinfo.comp_info[1].h_samp_factor = 1; // Cb |
1542 | 0 | cinfo.comp_info[1].v_samp_factor = 1; |
1543 | 0 | cinfo.comp_info[2].h_samp_factor = 1; // Cr |
1544 | 0 | cinfo.comp_info[2].v_samp_factor = 1; |
1545 | 0 | } |
1546 | 0 | } |
1547 | | |
1548 | | // Step 4: set quality |
1549 | | // the first 7 bits are reserved for low level quality settings |
1550 | | // the other bits are high level (i.e. enum-ish) |
1551 | |
|
1552 | 0 | int quality; |
1553 | |
|
1554 | 0 | if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) { |
1555 | 0 | quality = 10; |
1556 | 0 | } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) { |
1557 | 0 | quality = 25; |
1558 | 0 | } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) { |
1559 | 0 | quality = 50; |
1560 | 0 | } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) { |
1561 | 0 | quality = 75; |
1562 | 0 | } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) { |
1563 | 0 | quality = 100; |
1564 | 0 | } else { |
1565 | 0 | if ((flags & 0x7F) == 0) { |
1566 | 0 | quality = 75; |
1567 | 0 | } else { |
1568 | 0 | quality = flags & 0x7F; |
1569 | 0 | } |
1570 | 0 | } |
1571 | |
|
1572 | 0 | jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */ |
1573 | | |
1574 | | // Step 5: Start compressor |
1575 | |
|
1576 | 0 | jpeg_start_compress(&cinfo, TRUE); |
1577 | | |
1578 | | // Step 6: Write special markers |
1579 | | |
1580 | 0 | if ((flags & JPEG_BASELINE) != JPEG_BASELINE) { |
1581 | 0 | write_markers(&cinfo, dib); |
1582 | 0 | } |
1583 | | |
1584 | | // Step 7: while (scan lines remain to be written) |
1585 | |
|
1586 | 0 | if(color_type == FIC_RGB) { |
1587 | | // 24-bit RGB image : need to swap red and blue channels |
1588 | 0 | unsigned pitch = FreeImage_GetPitch(dib); |
1589 | 0 | BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE)); |
1590 | 0 | if (target == NULL) { |
1591 | 0 | throw FI_MSG_ERROR_MEMORY; |
1592 | 0 | } |
1593 | | |
1594 | 0 | while (cinfo.next_scanline < cinfo.image_height) { |
1595 | | // get a copy of the scanline |
1596 | 0 | memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch); |
1597 | 0 | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR |
1598 | | // swap R and B channels |
1599 | 0 | BYTE *target_p = target; |
1600 | 0 | for(unsigned x = 0; x < cinfo.image_width; x++) { |
1601 | 0 | INPLACESWAP(target_p[0], target_p[2]); |
1602 | 0 | target_p += 3; |
1603 | 0 | } |
1604 | 0 | #endif |
1605 | | // write the scanline |
1606 | 0 | jpeg_write_scanlines(&cinfo, &target, 1); |
1607 | 0 | } |
1608 | 0 | free(target); |
1609 | 0 | } |
1610 | 0 | else if(color_type == FIC_CMYK) { |
1611 | 0 | unsigned pitch = FreeImage_GetPitch(dib); |
1612 | 0 | BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE)); |
1613 | 0 | if (target == NULL) { |
1614 | 0 | throw FI_MSG_ERROR_MEMORY; |
1615 | 0 | } |
1616 | | |
1617 | 0 | while (cinfo.next_scanline < cinfo.image_height) { |
1618 | | // get a copy of the scanline |
1619 | 0 | memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch); |
1620 | | |
1621 | 0 | BYTE *target_p = target; |
1622 | 0 | for(unsigned x = 0; x < cinfo.image_width; x++) { |
1623 | | // CMYK pixels are inverted |
1624 | 0 | target_p[0] = ~target_p[0]; // C |
1625 | 0 | target_p[1] = ~target_p[1]; // M |
1626 | 0 | target_p[2] = ~target_p[2]; // Y |
1627 | 0 | target_p[3] = ~target_p[3]; // K |
1628 | |
|
1629 | 0 | target_p += 4; |
1630 | 0 | } |
1631 | | |
1632 | | // write the scanline |
1633 | 0 | jpeg_write_scanlines(&cinfo, &target, 1); |
1634 | 0 | } |
1635 | 0 | free(target); |
1636 | 0 | } |
1637 | 0 | else if(color_type == FIC_MINISBLACK) { |
1638 | | // 8-bit standard greyscale images |
1639 | 0 | while (cinfo.next_scanline < cinfo.image_height) { |
1640 | 0 | JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); |
1641 | |
|
1642 | 0 | jpeg_write_scanlines(&cinfo, &b, 1); |
1643 | 0 | } |
1644 | 0 | } |
1645 | 0 | else if(color_type == FIC_PALETTE) { |
1646 | | // 8-bit palettized images are converted to 24-bit images |
1647 | 0 | RGBQUAD *palette = FreeImage_GetPalette(dib); |
1648 | 0 | BYTE *target = (BYTE*)malloc(cinfo.image_width * 3); |
1649 | 0 | if (target == NULL) { |
1650 | 0 | throw FI_MSG_ERROR_MEMORY; |
1651 | 0 | } |
1652 | | |
1653 | 0 | while (cinfo.next_scanline < cinfo.image_height) { |
1654 | 0 | BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); |
1655 | 0 | FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette); |
1656 | |
|
1657 | 0 | #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR |
1658 | | // swap R and B channels |
1659 | 0 | BYTE *target_p = target; |
1660 | 0 | for(unsigned x = 0; x < cinfo.image_width; x++) { |
1661 | 0 | INPLACESWAP(target_p[0], target_p[2]); |
1662 | 0 | target_p += 3; |
1663 | 0 | } |
1664 | 0 | #endif |
1665 | | |
1666 | |
|
1667 | 0 | jpeg_write_scanlines(&cinfo, &target, 1); |
1668 | 0 | } |
1669 | |
|
1670 | 0 | free(target); |
1671 | 0 | } |
1672 | 0 | else if(color_type == FIC_MINISWHITE) { |
1673 | | // reverse 8-bit greyscale image, so reverse grey value on the fly |
1674 | 0 | unsigned i; |
1675 | 0 | BYTE reverse[256]; |
1676 | 0 | BYTE *target = (BYTE *)malloc(cinfo.image_width); |
1677 | 0 | if (target == NULL) { |
1678 | 0 | throw FI_MSG_ERROR_MEMORY; |
1679 | 0 | } |
1680 | | |
1681 | 0 | for(i = 0; i < 256; i++) { |
1682 | 0 | reverse[i] = (BYTE)(255 - i); |
1683 | 0 | } |
1684 | |
|
1685 | 0 | while(cinfo.next_scanline < cinfo.image_height) { |
1686 | 0 | BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); |
1687 | 0 | for(i = 0; i < cinfo.image_width; i++) { |
1688 | 0 | target[i] = reverse[ source[i] ]; |
1689 | 0 | } |
1690 | 0 | jpeg_write_scanlines(&cinfo, &target, 1); |
1691 | 0 | } |
1692 | |
|
1693 | 0 | free(target); |
1694 | 0 | } |
1695 | | |
1696 | | // Step 8: Finish compression |
1697 | | |
1698 | 0 | jpeg_finish_compress(&cinfo); |
1699 | | |
1700 | | // Step 9: release JPEG compression object |
1701 | |
|
1702 | 0 | jpeg_destroy_compress(&cinfo); |
1703 | |
|
1704 | 0 | return TRUE; |
1705 | |
|
1706 | 0 | } catch (const char *text) { |
1707 | 0 | if(text) { |
1708 | 0 | FreeImage_OutputMessageProc(s_format_id, text); |
1709 | 0 | } |
1710 | 0 | return FALSE; |
1711 | 0 | } |
1712 | 0 | } |
1713 | | |
1714 | 0 | return FALSE; |
1715 | 0 | } |
1716 | | |
1717 | | // ========================================================== |
1718 | | // Init |
1719 | | // ========================================================== |
1720 | | |
1721 | | void DLL_CALLCONV |
1722 | 2 | InitJPEG(Plugin *plugin, int format_id) { |
1723 | 2 | s_format_id = format_id; |
1724 | | |
1725 | 2 | plugin->format_proc = Format; |
1726 | 2 | plugin->description_proc = Description; |
1727 | 2 | plugin->extension_proc = Extension; |
1728 | 2 | plugin->regexpr_proc = RegExpr; |
1729 | 2 | plugin->open_proc = NULL; |
1730 | 2 | plugin->close_proc = NULL; |
1731 | 2 | plugin->pagecount_proc = NULL; |
1732 | | plugin->pagecapability_proc = NULL; |
1733 | 2 | plugin->load_proc = Load; |
1734 | 2 | plugin->save_proc = Save; |
1735 | 2 | plugin->validate_proc = Validate; |
1736 | 2 | plugin->mime_proc = MimeType; |
1737 | 2 | plugin->supports_export_bpp_proc = SupportsExportDepth; |
1738 | 2 | plugin->supports_export_type_proc = SupportsExportType; |
1739 | 2 | plugin->supports_icc_profiles_proc = SupportsICCProfiles; |
1740 | 2 | plugin->supports_no_pixels_proc = SupportsNoPixels; |
1741 | 2 | } |