/src/graphicsmagick/coders/jpeg.c
Line | Count | Source |
1 | | /* |
2 | | % Copyright (C) 2003-2025 GraphicsMagick Group |
3 | | % Copyright (C) 2002 ImageMagick Studio |
4 | | % Copyright 1991-1999 E. I. du Pont de Nemours and Company |
5 | | % |
6 | | % This program is covered by multiple licenses, which are described in |
7 | | % Copyright.txt. You should have received a copy of Copyright.txt with this |
8 | | % package; otherwise see http://www.graphicsmagick.org/www/Copyright.html. |
9 | | % |
10 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
11 | | % % |
12 | | % % |
13 | | % % |
14 | | % JJJJJ PPPP EEEEE GGGG % |
15 | | % J P P E G % |
16 | | % J PPPP EEE G GG % |
17 | | % J J P E G G % |
18 | | % JJJ P EEEEE GGG % |
19 | | % % |
20 | | % % |
21 | | % Read/Write JPEG Image Format. % |
22 | | % % |
23 | | % % |
24 | | % Software Design % |
25 | | % John Cristy % |
26 | | % July 1992 % |
27 | | % % |
28 | | % % |
29 | | % % |
30 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
31 | | % |
32 | | % This software is based in part on the work of the Independent JPEG Group. |
33 | | % See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and |
34 | | % licensing restrictions. Blob support contributed by Glenn Randers-Pehrson. |
35 | | % |
36 | | % |
37 | | */ |
38 | | |
39 | | /* |
40 | | Include declarations. |
41 | | */ |
42 | | #include "magick/studio.h" |
43 | | #include "magick/analyze.h" |
44 | | #include "magick/attribute.h" |
45 | | #include "magick/blob.h" |
46 | | #include "magick/colormap.h" |
47 | | #include "magick/enum_strings.h" |
48 | | #include "magick/log.h" |
49 | | #include "magick/magick.h" |
50 | | #include "magick/monitor.h" |
51 | | #include "magick/pixel_cache.h" |
52 | | #include "magick/profile.h" |
53 | | #include "magick/resource.h" |
54 | | #include "magick/utility.h" |
55 | | #include "magick/static.h" |
56 | | |
57 | | /* |
58 | | Forward declarations. |
59 | | */ |
60 | | static unsigned int |
61 | | WriteJPEGImage(const ImageInfo *,Image *); |
62 | | |
63 | | /* |
64 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
65 | | % % |
66 | | % % |
67 | | % % |
68 | | % I s J P E G % |
69 | | % % |
70 | | % % |
71 | | % % |
72 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
73 | | % |
74 | | % Method IsJPEG returns True if the image format type, identified by the |
75 | | % magick string, is JPEG. |
76 | | % |
77 | | % The format of the IsJPEG method is: |
78 | | % |
79 | | % unsigned int IsJPEG(const unsigned char *magick,const size_t length) |
80 | | % |
81 | | % A description of each parameter follows: |
82 | | % |
83 | | % o status: Method IsJPEG returns True if the image format type is JPEG. |
84 | | % |
85 | | % o magick: This string is generally the first few bytes of an image file |
86 | | % or blob. |
87 | | % |
88 | | % o length: Specifies the length of the magick string. |
89 | | % |
90 | | % |
91 | | */ |
92 | | static unsigned int IsJPEG(const unsigned char *magick,const size_t length) |
93 | 0 | { |
94 | 0 | if (length < 3) |
95 | 0 | return(False); |
96 | 0 | if (memcmp(magick,"\377\330\377",3) == 0) |
97 | 0 | return(True); |
98 | 0 | return(False); |
99 | 0 | } |
100 | | |
101 | | #if defined(HasJPEG) |
102 | | #define JPEG_INTERNAL_OPTIONS |
103 | | /* |
104 | | Avoid conflicting typedef for INT32 |
105 | | */ |
106 | | #if defined(__MINGW32__) |
107 | | # define XMD_H 1 |
108 | | #endif |
109 | | /* |
110 | | The JPEG headers have the annoying problem that they define |
111 | | HAVE_STDLIB_H and we do too. The define isn't actually used |
112 | | so just undef it. |
113 | | */ |
114 | | #undef HAVE_STDLIB_H |
115 | | #include <setjmp.h> |
116 | | #include "jpeglib.h" |
117 | | #include "jerror.h" |
118 | | |
119 | | /* |
120 | | Define declarations. |
121 | | */ |
122 | 218k | #define ICC_MARKER (JPEG_APP0+2) |
123 | 218k | #define IPTC_MARKER (JPEG_APP0+13) |
124 | 66 | #define XML_MARKER (JPEG_APP0+1) |
125 | 2.08M | #define MaxBufferExtent 8192 |
126 | | #define JPEG_MARKER_MAX_SIZE 65533 |
127 | 682k | #define MaxWarningCount 3 |
128 | | |
129 | | /* |
130 | | Set to 1 to use libjpeg callback for progress indication. This is |
131 | | not enabled by default since it outputs multiple progress |
132 | | indications, which may be confusing for the user. However, the |
133 | | libjpeg method provides more detailed progress. |
134 | | */ |
135 | | #define USE_LIBJPEG_PROGRESS 0 /* Use libjpeg callback for progress */ |
136 | | |
137 | | static const char xmp_std_header[]="http://ns.adobe.com/xap/1.0/"; |
138 | | |
139 | | |
140 | | /* |
141 | | Struct to lessen the impact of multiple sample types |
142 | | |
143 | | This assumes a normal architecture where pointer size is consistent. |
144 | | */ |
145 | | typedef struct |
146 | | { |
147 | | union |
148 | | { |
149 | | void *v; |
150 | | JSAMPLE *j; |
151 | | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
152 | | J12SAMPLE *j12; |
153 | | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
154 | | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
155 | | J16SAMPLE *j16; |
156 | | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
157 | | } t; |
158 | | } magick_jpeg_pixels_t; |
159 | | |
160 | | typedef struct _DestinationManager |
161 | | { |
162 | | struct jpeg_destination_mgr |
163 | | manager; |
164 | | |
165 | | Image |
166 | | *image; |
167 | | |
168 | | JOCTET |
169 | | *buffer; |
170 | | |
171 | | } DestinationManager; |
172 | | |
173 | | typedef struct _MagickClientData |
174 | | { |
175 | | Image |
176 | | *image; |
177 | | |
178 | | MagickBool |
179 | | ping; |
180 | | |
181 | | MagickBool |
182 | | completed; |
183 | | |
184 | | jmp_buf |
185 | | error_recovery; |
186 | | |
187 | | unsigned int |
188 | | max_warning_count; |
189 | | |
190 | | magick_uint16_t |
191 | | warning_counts[JMSG_LASTMSGCODE]; |
192 | | |
193 | | int |
194 | | max_scan_number; |
195 | | |
196 | | ProfileInfo |
197 | | profiles[16]; |
198 | | |
199 | | unsigned char |
200 | | buffer[65537+200]; |
201 | | |
202 | | magick_jpeg_pixels_t |
203 | | *jpeg_pixels; |
204 | | |
205 | | } MagickClientData; |
206 | | |
207 | | typedef struct _SourceManager |
208 | | { |
209 | | struct jpeg_source_mgr |
210 | | manager; |
211 | | |
212 | | Image |
213 | | *image; |
214 | | |
215 | | JOCTET |
216 | | *buffer; |
217 | | |
218 | | boolean |
219 | | start_of_blob; |
220 | | } SourceManager; |
221 | | |
222 | | static MagickClientData *AllocateMagickClientData(void) |
223 | 707k | { |
224 | 707k | MagickClientData |
225 | 707k | *client_data; |
226 | | |
227 | 707k | client_data = MagickAllocateClearedMemory(MagickClientData *, sizeof(MagickClientData)); |
228 | | |
229 | 707k | return client_data; |
230 | 707k | } |
231 | | |
232 | | static MagickClientData *FreeMagickClientData(MagickClientData *client_data) |
233 | 707k | { |
234 | 707k | unsigned int |
235 | 707k | i; |
236 | | |
237 | | /* Free profiles data */ |
238 | 707k | if (client_data != (MagickClientData *) NULL) |
239 | 707k | { |
240 | 12.0M | for (i=0 ; i < ArraySize(client_data->profiles); i++) |
241 | 11.3M | { |
242 | 11.3M | MagickFreeMemory(client_data->profiles[i].name); |
243 | 11.3M | MagickFreeResourceLimitedMemory(unsigned char *,client_data->profiles[i].info); |
244 | 11.3M | } |
245 | 707k | if (client_data->jpeg_pixels != (magick_jpeg_pixels_t *) NULL) |
246 | 682k | MagickFreeResourceLimitedMemory(void *,client_data->jpeg_pixels->t.v); |
247 | | |
248 | 707k | MagickFreeMemory(client_data); |
249 | 707k | } |
250 | 707k | return client_data; |
251 | 707k | } |
252 | | |
253 | | /* Append named profile to profiles in client data. */ |
254 | | static MagickPassFail |
255 | | AppendProfile(MagickClientData *client_data, |
256 | | const char *name, |
257 | | const unsigned char *profile_chunk, |
258 | | const size_t chunk_length) |
259 | 513k | { |
260 | 513k | unsigned int |
261 | 513k | i; |
262 | | |
263 | | /* |
264 | | If entry with matching name is found, then add/append data to |
265 | | profile 'info' and update profile length |
266 | | */ |
267 | 1.63M | for (i=0 ; i < ArraySize(client_data->profiles); i++) |
268 | 1.63M | { |
269 | 1.63M | ProfileInfo *profile=&client_data->profiles[i]; |
270 | 1.63M | if (!profile->name) |
271 | 71.1k | break; |
272 | 1.56M | if (strcmp(profile->name,name) == 0) |
273 | 442k | { |
274 | 442k | const size_t new_length = profile->length+chunk_length; |
275 | 442k | unsigned char *info = MagickReallocateResourceLimitedMemory(unsigned char *, |
276 | 442k | profile->info,new_length); |
277 | 442k | if (info != (unsigned char *) NULL) |
278 | 442k | { |
279 | 442k | profile->info = info; |
280 | 442k | (void) memcpy(profile->info+profile->length,profile_chunk,chunk_length); |
281 | 442k | profile->length=new_length; |
282 | 442k | return MagickPass; |
283 | 442k | } |
284 | 442k | } |
285 | 1.56M | } |
286 | | |
287 | | |
288 | | /* |
289 | | If no matching entry, then find unallocated entry, add data to |
290 | | profile 'info' and update profile length |
291 | | */ |
292 | 105k | for (i=0 ; i < ArraySize(client_data->profiles); i++) |
293 | 105k | { |
294 | 105k | ProfileInfo *profile=&client_data->profiles[i]; |
295 | 105k | if (profile->name) |
296 | 34.8k | continue; |
297 | 71.1k | profile->name=AcquireString(name); |
298 | 71.1k | profile->info=MagickAllocateResourceLimitedMemory(unsigned char*,chunk_length); |
299 | 71.1k | if (!profile->name || !profile->info) |
300 | 0 | { |
301 | 0 | MagickFreeMemory(profile->name); |
302 | 0 | MagickFreeResourceLimitedMemory(unsigned char *,profile->info); |
303 | 0 | break; |
304 | 0 | } |
305 | 71.1k | (void) memcpy(profile->info,profile_chunk,chunk_length); |
306 | 71.1k | profile->length=chunk_length; |
307 | 71.1k | return MagickPass; |
308 | 71.1k | } |
309 | 0 | return MagickFail; |
310 | 71.1k | } |
311 | | |
312 | | /* |
313 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
314 | | % % |
315 | | % % |
316 | | % % |
317 | | % R e a d J P E G I m a g e % |
318 | | % % |
319 | | % % |
320 | | % % |
321 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
322 | | % |
323 | | % Method ReadJPEGImage reads a JPEG image file and returns it. It allocates |
324 | | % the memory necessary for the new Image structure and returns a pointer to |
325 | | % the new image. |
326 | | % |
327 | | % The format of the ReadJPEGImage method is: |
328 | | % |
329 | | % Image *ReadJPEGImage(const ImageInfo *image_info, |
330 | | % ExceptionInfo *exception) |
331 | | % |
332 | | % A description of each parameter follows: |
333 | | % |
334 | | % o image: Method ReadJPEGImage returns a pointer to the image after |
335 | | % reading. A null image is returned if there is a memory shortage or |
336 | | % if the image cannot be read. |
337 | | % |
338 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
339 | | % |
340 | | % o exception: return any errors or warnings in this structure. |
341 | | % |
342 | | % |
343 | | */ |
344 | | |
345 | | |
346 | | /* |
347 | | Format a libjpeg warning or trace event while decoding. Warnings |
348 | | are converted to GraphicsMagick warning exceptions while traces are |
349 | | optionally logged. |
350 | | |
351 | | JPEG message codes range from 0 to JMSG_LASTMSGCODE |
352 | | */ |
353 | | static void JPEGDecodeMessageHandler(j_common_ptr jpeg_info,int msg_level) |
354 | 14.0M | { |
355 | 14.0M | char |
356 | 14.0M | message[JMSG_LENGTH_MAX]; |
357 | | |
358 | 14.0M | struct jpeg_error_mgr |
359 | 14.0M | *err; |
360 | | |
361 | 14.0M | MagickClientData |
362 | 14.0M | *client_data; |
363 | | |
364 | 14.0M | Image |
365 | 14.0M | *image; |
366 | | |
367 | 14.0M | message[0]='\0'; |
368 | 14.0M | err=jpeg_info->err; |
369 | 14.0M | client_data=(MagickClientData *) jpeg_info->client_data; |
370 | 14.0M | image=client_data->image; |
371 | | /* msg_level is -1 for warnings, 0 and up for trace messages. */ |
372 | 14.0M | if (msg_level < 0) |
373 | 2.58M | { |
374 | 2.58M | unsigned int strikes = 0; |
375 | | /* A warning */ |
376 | 2.58M | (err->format_message)(jpeg_info,message); |
377 | | |
378 | 2.58M | if ((err->msg_code >= 0) && |
379 | 2.58M | ((size_t) err->msg_code < ArraySize(client_data->warning_counts))) |
380 | 2.58M | { |
381 | 2.58M | client_data->warning_counts[err->msg_code]++; |
382 | 2.58M | strikes=client_data->warning_counts[err->msg_code]; |
383 | 2.58M | } |
384 | | |
385 | 2.58M | if (image->logging) |
386 | 2.58M | { |
387 | 2.58M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
388 | 2.58M | "[%s] JPEG Warning[%u]: \"%s\"" |
389 | 2.58M | " (code=%d " |
390 | 2.58M | "parms=0x%02x,0x%02x," |
391 | 2.58M | "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)", |
392 | 2.58M | image->filename, |
393 | 2.58M | strikes, |
394 | 2.58M | message,err->msg_code, |
395 | 2.58M | err->msg_parm.i[0], err->msg_parm.i[1], |
396 | 2.58M | err->msg_parm.i[2], err->msg_parm.i[3], |
397 | 2.58M | err->msg_parm.i[4], err->msg_parm.i[5], |
398 | 2.58M | err->msg_parm.i[6], err->msg_parm.i[7]); |
399 | 2.58M | } |
400 | 2.58M | if (strikes > client_data->max_warning_count) |
401 | 118k | { |
402 | 118k | ThrowException2(&image->exception,CorruptImageError,(char *) message, |
403 | 118k | image->filename); |
404 | 118k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
405 | 118k | "Longjmp error recovery"); |
406 | 118k | longjmp(client_data->error_recovery,1); |
407 | 0 | SignalHandlerExit(EXIT_FAILURE); |
408 | 118k | } |
409 | | |
410 | 2.46M | if ((err->num_warnings == 0) || |
411 | 1.80M | (err->trace_level >= 3)) |
412 | 659k | ThrowException2(&image->exception,CorruptImageWarning,message, |
413 | 2.46M | image->filename); |
414 | | /* JWRN_JPEG_EOF - "Premature end of JPEG file" */ |
415 | 2.46M | err->num_warnings++; |
416 | 2.46M | return /* False */; |
417 | 2.58M | } |
418 | 11.4M | else |
419 | 11.4M | { |
420 | | /* A trace message */ |
421 | 11.4M | if ((image->logging) && (msg_level >= err->trace_level)) |
422 | 11.4M | { |
423 | 11.4M | (err->format_message)(jpeg_info,message); |
424 | 11.4M | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
425 | 11.4M | "[%s] JPEG Trace: \"%s\"",image->filename, |
426 | 11.4M | message); |
427 | 11.4M | } |
428 | 11.4M | } |
429 | 11.4M | return /* True */; |
430 | 14.0M | } |
431 | | |
432 | | static void JPEGDecodeProgressMonitor(j_common_ptr cinfo) |
433 | 15.5M | { |
434 | 15.5M | MagickClientData *client_data = (MagickClientData *) cinfo->client_data; |
435 | 15.5M | Image *image = client_data->image; |
436 | 15.5M | const int max_scan_number = client_data->max_scan_number; |
437 | | |
438 | | #if USE_LIBJPEG_PROGRESS |
439 | | { |
440 | | struct jpeg_progress_mgr *p = cinfo->progress; |
441 | | |
442 | | #if 0 |
443 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
444 | | "Progress: pass_counter=%ld, pass_limit=%ld," |
445 | | " completed_passes=%d, total_passes=%d, filename=%s", |
446 | | p->pass_counter, p->pass_limit, |
447 | | p->completed_passes, p->total_passes, image->filename); |
448 | | #endif |
449 | | |
450 | | if (QuantumTick(p->pass_counter,p->pass_limit)) |
451 | | if (!MagickMonitorFormatted(p->pass_counter,p->pass_limit,&image->exception, |
452 | | "[%s] Loading image: %lux%lu (pass %d of %d)... ", |
453 | | image->filename, |
454 | | image->columns,image->rows, |
455 | | p->completed_passes+1, p->total_passes)) |
456 | | { |
457 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
458 | | "Quitting (longjmp) due to progress monitor"); |
459 | | longjmp(client_data->error_recovery,1); |
460 | | SignalHandlerExit(EXIT_FAILURE); |
461 | | } |
462 | | } |
463 | | #endif /* USE_LIBJPEG_PROGRESS */ |
464 | | |
465 | 15.5M | if (cinfo->is_decompressor) |
466 | 15.5M | { |
467 | 15.5M | int scan_no = ((j_decompress_ptr) cinfo)->input_scan_number; |
468 | | |
469 | 15.5M | if (scan_no > max_scan_number) |
470 | 7 | { |
471 | 7 | char message[MaxTextExtent]; |
472 | 7 | FormatString(message,"Scan number %d exceeds maximum scans (%d)", |
473 | 7 | scan_no, max_scan_number); |
474 | 7 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"%s", message); |
475 | 7 | ThrowException2(&image->exception,CorruptImageError,(char *) message, |
476 | 7 | image->filename); |
477 | 7 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
478 | 7 | "Longjmp error recovery"); |
479 | 7 | longjmp(client_data->error_recovery,1); |
480 | 0 | SignalHandlerExit(EXIT_FAILURE); |
481 | 7 | } |
482 | 15.5M | } |
483 | 15.5M | } |
484 | | |
485 | | static boolean FillInputBuffer(j_decompress_ptr cinfo) |
486 | 1.38M | { |
487 | 1.38M | SourceManager |
488 | 1.38M | *source; |
489 | | |
490 | 1.38M | source=(SourceManager *) cinfo->src; |
491 | 1.38M | source->manager.bytes_in_buffer= |
492 | 1.38M | ReadBlob(source->image,MaxBufferExtent,(char *) source->buffer); |
493 | 1.38M | if (source->manager.bytes_in_buffer == 0) |
494 | 654k | { |
495 | 654k | if (source->start_of_blob) |
496 | 0 | ERREXIT(cinfo,JERR_INPUT_EMPTY); |
497 | 654k | WARNMS(cinfo,JWRN_JPEG_EOF); |
498 | 654k | source->buffer[0]=(JOCTET) 0xff; |
499 | 654k | source->buffer[1]=(JOCTET) JPEG_EOI; |
500 | 654k | source->manager.bytes_in_buffer=2; |
501 | 654k | } |
502 | 1.38M | source->manager.next_input_byte=source->buffer; |
503 | 1.38M | source->start_of_blob=FALSE; |
504 | 1.38M | return(TRUE); |
505 | 1.38M | } |
506 | | |
507 | | static int GetCharacter(j_decompress_ptr jpeg_info) |
508 | 103M | { |
509 | 103M | if (jpeg_info->src->bytes_in_buffer == 0) |
510 | 162k | if ((!((*jpeg_info->src->fill_input_buffer)(jpeg_info))) || |
511 | 126k | (jpeg_info->src->bytes_in_buffer == 0)) |
512 | 0 | return EOF; |
513 | 103M | jpeg_info->src->bytes_in_buffer--; |
514 | 103M | return(GETJOCTET(*jpeg_info->src->next_input_byte++)); |
515 | 103M | } |
516 | | |
517 | | static void InitializeSource(j_decompress_ptr cinfo) |
518 | 681k | { |
519 | 681k | SourceManager |
520 | 681k | *source; |
521 | | |
522 | 681k | source=(SourceManager *) cinfo->src; |
523 | 681k | source->start_of_blob=TRUE; |
524 | 681k | } |
525 | | |
526 | | /* |
527 | | Format and report a libjpeg error event. Errors are reported via a |
528 | | GraphicsMagick error exception. The function terminates with |
529 | | longjmp() so it never returns to the caller. |
530 | | */ |
531 | | static void JPEGErrorHandler(j_common_ptr jpeg_info) MAGICK_FUNC_NORETURN; |
532 | | |
533 | | static void JPEGErrorHandler(j_common_ptr jpeg_info) |
534 | 371k | { |
535 | 371k | char |
536 | 371k | message[JMSG_LENGTH_MAX]; |
537 | | |
538 | 371k | struct jpeg_error_mgr |
539 | 371k | *err; |
540 | | |
541 | 371k | MagickClientData |
542 | 371k | *client_data; |
543 | | |
544 | 371k | Image |
545 | 371k | *image; |
546 | | |
547 | 371k | message[0]='\0'; |
548 | 371k | err=jpeg_info->err; |
549 | 371k | client_data=(MagickClientData *) jpeg_info->client_data; |
550 | 371k | image=client_data->image; |
551 | 371k | (err->format_message)(jpeg_info,message); |
552 | 371k | if (image->logging) |
553 | 371k | { |
554 | 371k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
555 | 371k | "[%s] JPEG Error: \"%s\" (code=%d, " |
556 | 371k | "parms=0x%02x,0x%02x," |
557 | 371k | "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)", |
558 | 371k | image->filename,message,err->msg_code, |
559 | 371k | err->msg_parm.i[0], err->msg_parm.i[1], |
560 | 371k | err->msg_parm.i[2], err->msg_parm.i[3], |
561 | 371k | err->msg_parm.i[4], err->msg_parm.i[5], |
562 | 371k | err->msg_parm.i[6], err->msg_parm.i[7]); |
563 | 371k | } |
564 | 371k | if (client_data->completed) |
565 | 20.8k | ThrowException2(&image->exception,CoderWarning,(char *) message, |
566 | 371k | image->filename); |
567 | 351k | else |
568 | 351k | ThrowException2(&image->exception,CoderError,(char *) message, |
569 | 371k | image->filename); |
570 | 371k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Longjmp error recovery"); |
571 | 371k | longjmp(client_data->error_recovery,1); |
572 | 0 | SignalHandlerExit(EXIT_FAILURE); |
573 | 371k | } |
574 | | |
575 | | #define GetProfileLength(jpeg_info, length) \ |
576 | 915k | do { \ |
577 | 915k | int \ |
578 | 915k | _c; \ |
579 | 915k | \ |
580 | 915k | if (((_c = GetCharacter(jpeg_info)) != EOF) && (_c >= 0)) \ |
581 | 915k | { \ |
582 | 915k | length=(size_t) _c*256; \ |
583 | 915k | if (((_c = GetCharacter(jpeg_info)) != EOF) && (_c >= 0)) \ |
584 | 915k | length+=(size_t)_c; \ |
585 | 915k | else \ |
586 | 915k | length=0; \ |
587 | 915k | } \ |
588 | 915k | else \ |
589 | 915k | { \ |
590 | 0 | length=0; \ |
591 | 0 | } \ |
592 | 915k | } while(0) |
593 | | |
594 | | static boolean ReadComment(j_decompress_ptr jpeg_info) |
595 | 223k | { |
596 | 223k | char |
597 | 223k | *comment; |
598 | | |
599 | 223k | MagickClientData |
600 | 223k | *client_data; |
601 | | |
602 | 223k | Image |
603 | 223k | *image; |
604 | | |
605 | 223k | register char |
606 | 223k | *p; |
607 | | |
608 | 223k | size_t |
609 | 223k | i, |
610 | 223k | length; |
611 | | |
612 | 223k | int |
613 | 223k | c; |
614 | | |
615 | | /* |
616 | | Determine length of comment. |
617 | | */ |
618 | 223k | client_data=(MagickClientData *) jpeg_info->client_data; |
619 | 223k | image=client_data->image; |
620 | 223k | GetProfileLength(jpeg_info, length); |
621 | 223k | if (length <= 2) |
622 | 8.20k | return(True); |
623 | 215k | length-=2; |
624 | 215k | comment=(char *) client_data->buffer; |
625 | | /* |
626 | | Read comment. |
627 | | */ |
628 | 215k | p=comment; |
629 | 17.6M | for (i=0; i < length; i++) |
630 | 17.4M | { |
631 | 17.4M | if ((c=GetCharacter(jpeg_info)) == EOF) |
632 | 0 | break; |
633 | 17.4M | *p=c; |
634 | 17.4M | p++; |
635 | 17.4M | } |
636 | 215k | *p='\0'; |
637 | 215k | (void) SetImageAttribute(image,"comment",comment); |
638 | 215k | return(True); |
639 | 223k | } |
640 | | |
641 | | static boolean ReadGenericProfile(j_decompress_ptr jpeg_info) |
642 | 565k | { |
643 | 565k | MagickClientData |
644 | 565k | *client_data; |
645 | | |
646 | 565k | size_t |
647 | 565k | header_length=0, |
648 | 565k | length; |
649 | | |
650 | 565k | register size_t |
651 | 565k | i; |
652 | | |
653 | 565k | char |
654 | 565k | profile_name[MaxTextExtent]; |
655 | | |
656 | 565k | unsigned char |
657 | 565k | *profile; |
658 | | |
659 | 565k | int |
660 | 565k | c, |
661 | 565k | marker; |
662 | | |
663 | | /* |
664 | | Determine length of generic profile. |
665 | | */ |
666 | 565k | GetProfileLength(jpeg_info, length); |
667 | 565k | if (length <= 2) |
668 | 52.6k | return(True); |
669 | 512k | length-=2; |
670 | | |
671 | | /* |
672 | | jpeg_info->unread_marker ('int') is either zero or the code of a |
673 | | JPEG marker that has been read from the data source, but has not |
674 | | yet been processed. The underlying type for a marker appears to |
675 | | be UINT8 (JPEG_COM, or JPEG_APP0+n). |
676 | | |
677 | | Unexpected markers are prevented due to registering for specific |
678 | | markers we are interested in via jpeg_set_marker_processor(). |
679 | | |
680 | | #define JPEG_APP0 0xE0 |
681 | | #define JPEG_COM 0xFE |
682 | | |
683 | | #define ICC_MARKER (JPEG_APP0+2) |
684 | | #define IPTC_MARKER (JPEG_APP0+13) |
685 | | #define XML_MARKER (JPEG_APP0+1) |
686 | | */ |
687 | 512k | marker=jpeg_info->unread_marker-JPEG_APP0; |
688 | | |
689 | | /* |
690 | | Compute generic profile name. |
691 | | */ |
692 | 512k | FormatString(profile_name,"APP%d",marker); |
693 | | |
694 | | /* |
695 | | Obtain Image. |
696 | | */ |
697 | 512k | client_data=(MagickClientData *) jpeg_info->client_data; |
698 | | |
699 | | /* |
700 | | Copy profile from JPEG to allocated memory. |
701 | | */ |
702 | 512k | profile=client_data->buffer; |
703 | | |
704 | 56.1M | for (i=0 ; i < length ; i++) |
705 | 55.6M | { |
706 | 55.6M | if ((c=GetCharacter(jpeg_info)) != EOF) |
707 | 55.6M | profile[i]=c; |
708 | 9.32k | else |
709 | 9.32k | break; |
710 | 55.6M | } |
711 | 512k | if (i != length) |
712 | 0 | return True; |
713 | | |
714 | | /* |
715 | | Detect EXIF and XMP profiles. |
716 | | */ |
717 | 512k | if ((marker == 1) && (length > 4) && |
718 | 70.0k | (strncmp((char *) profile,"Exif",4) == 0)) |
719 | 39.0k | { |
720 | 39.0k | FormatString(profile_name,"EXIF"); |
721 | 39.0k | } |
722 | 473k | else if (((marker == 1) && (length > strlen(xmp_std_header)+1)) && |
723 | 14.9k | (memcmp(profile, xmp_std_header, strlen(xmp_std_header)+1) == 0)) |
724 | 1.82k | { |
725 | | /* |
726 | | XMP is required to fit in one 64KB chunk. Strip off its JPEG |
727 | | namespace header. |
728 | | */ |
729 | 1.82k | header_length=strlen(xmp_std_header)+1; |
730 | 1.82k | FormatString((char *) profile_name,"XMP"); |
731 | 1.82k | } |
732 | | |
733 | | /* |
734 | | Store profile in Image. |
735 | | */ |
736 | 512k | (void) AppendProfile(client_data,profile_name,profile+header_length, |
737 | 512k | length-header_length); |
738 | | |
739 | 512k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
740 | 512k | "Profile: %s, header %" MAGICK_SIZE_T_F "u bytes, " |
741 | 512k | "data %" MAGICK_SIZE_T_F "u bytes", |
742 | 512k | profile_name, (MAGICK_SIZE_T) header_length, |
743 | 512k | (MAGICK_SIZE_T) length-header_length); |
744 | | |
745 | 512k | return (True); |
746 | 512k | } |
747 | | |
748 | | static boolean ReadICCProfile(j_decompress_ptr jpeg_info) |
749 | 88.5k | { |
750 | 88.5k | char |
751 | 88.5k | magick[12]; |
752 | | |
753 | 88.5k | MagickClientData |
754 | 88.5k | *client_data; |
755 | | |
756 | 88.5k | unsigned char |
757 | 88.5k | *profile; |
758 | | |
759 | 88.5k | long |
760 | 88.5k | length; |
761 | | |
762 | 88.5k | register long |
763 | 88.5k | i; |
764 | | |
765 | 88.5k | int |
766 | 88.5k | c; |
767 | | |
768 | | /* |
769 | | Determine length of color profile. |
770 | | */ |
771 | 88.5k | GetProfileLength(jpeg_info, length); |
772 | 88.5k | length-=2; |
773 | 88.5k | if (length <= 14) |
774 | 65.2k | { |
775 | 87.3k | while (--length >= 0) |
776 | 22.0k | (void) GetCharacter(jpeg_info); |
777 | 65.2k | return(True); |
778 | 65.2k | } |
779 | 291k | for (i=0; i < 12; i++) |
780 | 268k | magick[i]=GetCharacter(jpeg_info); |
781 | 23.3k | if (LocaleCompare(magick,"ICC_PROFILE") != 0) |
782 | 19.2k | { |
783 | | /* |
784 | | Not a ICC profile, return. |
785 | | */ |
786 | 6.19M | for (i=0; i < length-12; i++) |
787 | 6.17M | (void) GetCharacter(jpeg_info); |
788 | 19.2k | return(True); |
789 | 19.2k | } |
790 | 4.07k | (void) GetCharacter(jpeg_info); /* id */ |
791 | 4.07k | (void) GetCharacter(jpeg_info); /* markers */ |
792 | 4.07k | length-=14; |
793 | 4.07k | client_data=(MagickClientData *) jpeg_info->client_data; |
794 | | |
795 | | /* |
796 | | Read color profile. |
797 | | */ |
798 | 4.07k | profile=client_data->buffer; |
799 | | |
800 | 4.07k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
801 | 4.07k | "ICC profile chunk: %ld bytes", |
802 | 4.07k | length); |
803 | | |
804 | 3.76M | for (i=0 ; i < length; i++) |
805 | 3.76M | { |
806 | 3.76M | if ((c=GetCharacter(jpeg_info)) != EOF) |
807 | 3.76M | profile[i]=c; |
808 | 212 | else |
809 | 212 | break; |
810 | 3.76M | } |
811 | 4.07k | if (i == length) |
812 | 2.04k | (void) AppendProfile(client_data,"ICM",profile,length); |
813 | | |
814 | 4.07k | return(True); |
815 | 23.3k | } |
816 | | |
817 | | static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info) |
818 | 38.2k | { |
819 | 38.2k | MagickClientData |
820 | 38.2k | *client_data; |
821 | | |
822 | 38.2k | Image |
823 | 38.2k | *image; |
824 | | |
825 | 38.2k | long |
826 | 38.2k | length, |
827 | 38.2k | tag_length; |
828 | | |
829 | 38.2k | unsigned char |
830 | 38.2k | *profile; |
831 | | |
832 | 38.2k | register long |
833 | 38.2k | i; |
834 | | |
835 | | #ifdef GET_ONLY_IPTC_DATA |
836 | | unsigned char |
837 | | tag[MaxTextExtent]; |
838 | | #endif |
839 | | |
840 | 38.2k | int |
841 | 38.2k | c; |
842 | | |
843 | | /* |
844 | | Determine length of binary data stored here. |
845 | | */ |
846 | 38.2k | GetProfileLength(jpeg_info, length); |
847 | 38.2k | length-=2; |
848 | 38.2k | if (length <= 0) |
849 | 3.02k | return(True); |
850 | 35.2k | tag_length=0; |
851 | | #ifdef GET_ONLY_IPTC_DATA |
852 | | *tag='\0'; |
853 | | #endif |
854 | 35.2k | client_data=(MagickClientData *) jpeg_info->client_data; |
855 | 35.2k | image=client_data->image; |
856 | | #ifdef GET_ONLY_IPTC_DATA |
857 | | /* |
858 | | Find the beginning of the IPTC portion of the binary data. |
859 | | */ |
860 | | for (*tag='\0'; length > 0; ) |
861 | | { |
862 | | *tag=GetCharacter(jpeg_info); |
863 | | *(tag+1)=GetCharacter(jpeg_info); |
864 | | length-=2; |
865 | | if ((*tag == 0x1c) && (*(tag+1) == 0x02)) |
866 | | break; |
867 | | } |
868 | | tag_length=2; |
869 | | #else |
870 | | /* |
871 | | Validate that this was written as a Photoshop resource format slug. |
872 | | */ |
873 | 35.2k | { |
874 | 35.2k | char |
875 | 35.2k | magick[MaxTextExtent]; |
876 | | |
877 | 352k | for (i=0; i < 10 && i < length; i++) |
878 | 316k | magick[i]=GetCharacter(jpeg_info); |
879 | 35.2k | magick[i]='\0'; |
880 | 35.2k | length-=i; |
881 | 35.2k | if (LocaleCompare(magick,"Photoshop ") != 0) |
882 | 22.9k | { |
883 | | /* |
884 | | Not a ICC profile, return. |
885 | | */ |
886 | 6.54M | for (i=0; i < length; i++) |
887 | 6.52M | (void) GetCharacter(jpeg_info); |
888 | 22.9k | return(True); |
889 | 22.9k | } |
890 | 35.2k | } |
891 | | /* |
892 | | Remove the version number. |
893 | | */ |
894 | 49.6k | for (i=0; i < 4 && i < length; i++) |
895 | 37.4k | (void) GetCharacter(jpeg_info); |
896 | 12.2k | length-=i; |
897 | 12.2k | tag_length=0; |
898 | 12.2k | #endif |
899 | 12.2k | if (length <= 0) |
900 | 989 | return(True); |
901 | | |
902 | 11.2k | if ((size_t) length+tag_length > sizeof(client_data->buffer)) |
903 | 0 | ThrowBinaryException(ResourceLimitError,MemoryAllocationFailed, |
904 | 11.2k | (char *) NULL); |
905 | 11.2k | profile=client_data->buffer; |
906 | | /* |
907 | | Read the payload of this binary data. |
908 | | */ |
909 | 11.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
910 | 11.2k | "Profile: IPTC, %ld bytes", |
911 | 11.2k | length); |
912 | | |
913 | 11.3M | for (i=0; i < length; i++) |
914 | 11.3M | { |
915 | 11.3M | if ((c=GetCharacter(jpeg_info)) != EOF) |
916 | 11.3M | profile[i]=c; |
917 | 395 | else |
918 | 395 | break; |
919 | 11.3M | } |
920 | 11.2k | if (i == length) |
921 | 8.63k | (void) AppendProfile(client_data,"IPTC",profile,length); |
922 | | |
923 | 11.2k | return(True); |
924 | 11.2k | } |
925 | | |
926 | | static void SkipInputData(j_decompress_ptr cinfo,long number_bytes) |
927 | 268k | { |
928 | 268k | SourceManager |
929 | 268k | *source; |
930 | | |
931 | 268k | if (number_bytes <= 0) |
932 | 0 | return; |
933 | 268k | source=(SourceManager *) cinfo->src; |
934 | 319k | while (number_bytes > (long) source->manager.bytes_in_buffer) |
935 | 50.8k | { |
936 | 50.8k | number_bytes-=(long) source->manager.bytes_in_buffer; |
937 | 50.8k | (void) FillInputBuffer(cinfo); |
938 | 50.8k | } |
939 | 268k | source->manager.next_input_byte+=(size_t) number_bytes; |
940 | 268k | source->manager.bytes_in_buffer-=(size_t) number_bytes; |
941 | 268k | } |
942 | | |
943 | | static void TerminateSource(j_decompress_ptr cinfo) |
944 | 136k | { |
945 | 136k | (void) cinfo; |
946 | 136k | } |
947 | | |
948 | | static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image) |
949 | 681k | { |
950 | 681k | SourceManager |
951 | 681k | *source; |
952 | | |
953 | 681k | cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) |
954 | 681k | ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager)); |
955 | 681k | source=(SourceManager *) cinfo->src; |
956 | 681k | source->buffer=(JOCTET *) (*cinfo->mem->alloc_small) |
957 | 681k | ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); |
958 | 681k | source=(SourceManager *) cinfo->src; |
959 | 681k | source->manager.init_source=InitializeSource; |
960 | 681k | source->manager.fill_input_buffer=FillInputBuffer; |
961 | 681k | source->manager.skip_input_data=SkipInputData; |
962 | 681k | source->manager.resync_to_restart=jpeg_resync_to_restart; |
963 | 681k | source->manager.term_source=TerminateSource; |
964 | 681k | source->manager.bytes_in_buffer=0; |
965 | 681k | source->manager.next_input_byte=NULL; |
966 | 681k | source->image=image; |
967 | 681k | } |
968 | | |
969 | | /* |
970 | | Estimate the IJG quality factor used when saving the file. |
971 | | */ |
972 | | static int |
973 | | EstimateJPEGQuality(const struct jpeg_decompress_struct *jpeg_info, |
974 | | Image *image) |
975 | 171k | { |
976 | 171k | int |
977 | 171k | save_quality; |
978 | | |
979 | 171k | register long |
980 | 171k | i; |
981 | | |
982 | 171k | save_quality=0; |
983 | | #if !defined(LIBJPEG_TURBO_VERSION_NUMBER) && defined(D_LOSSLESS_SUPPORTED) |
984 | | if (image->compression==LosslessJPEGCompression) |
985 | | { |
986 | | save_quality=100; |
987 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
988 | | "Quality: 100 (lossless)"); |
989 | | } |
990 | | else |
991 | | #endif |
992 | | |
993 | 171k | { |
994 | 171k | int |
995 | 171k | hashval, |
996 | 171k | sum; |
997 | | |
998 | | /* |
999 | | Log the JPEG quality that was used for compression. |
1000 | | */ |
1001 | 171k | sum=0; |
1002 | 855k | for (i=0; i < NUM_QUANT_TBLS; i++) |
1003 | 684k | { |
1004 | 684k | int |
1005 | 684k | j; |
1006 | | |
1007 | 684k | if (jpeg_info->quant_tbl_ptrs[i] != NULL) |
1008 | 11.4M | for (j=0; j < DCTSIZE2; j++) |
1009 | 11.2M | { |
1010 | 11.2M | UINT16 *c; |
1011 | 11.2M | c=jpeg_info->quant_tbl_ptrs[i]->quantval; |
1012 | 11.2M | sum+=c[j]; |
1013 | 11.2M | } |
1014 | 684k | } |
1015 | 171k | if ((jpeg_info->quant_tbl_ptrs[0] != NULL) && |
1016 | 83.4k | (jpeg_info->quant_tbl_ptrs[1] != NULL)) |
1017 | 21.2k | { |
1018 | 21.2k | int |
1019 | 21.2k | hash[] = |
1020 | 21.2k | { |
1021 | 21.2k | 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645, |
1022 | 21.2k | 632, 623, 613, 607, 600, 594, 589, 585, 581, 571, |
1023 | 21.2k | 555, 542, 529, 514, 494, 474, 457, 439, 424, 410, |
1024 | 21.2k | 397, 386, 373, 364, 351, 341, 334, 324, 317, 309, |
1025 | 21.2k | 299, 294, 287, 279, 274, 267, 262, 257, 251, 247, |
1026 | 21.2k | 243, 237, 232, 227, 222, 217, 213, 207, 202, 198, |
1027 | 21.2k | 192, 188, 183, 177, 173, 168, 163, 157, 153, 148, |
1028 | 21.2k | 143, 139, 132, 128, 125, 119, 115, 108, 104, 99, |
1029 | 21.2k | 94, 90, 84, 79, 74, 70, 64, 59, 55, 49, |
1030 | 21.2k | 45, 40, 34, 30, 25, 20, 15, 11, 6, 4, |
1031 | 21.2k | 0 |
1032 | 21.2k | }; |
1033 | | |
1034 | 21.2k | int |
1035 | 21.2k | sums[] = |
1036 | 21.2k | { |
1037 | 21.2k | 32640,32635,32266,31495,30665,29804,29146,28599,28104,27670, |
1038 | 21.2k | 27225,26725,26210,25716,25240,24789,24373,23946,23572,22846, |
1039 | 21.2k | 21801,20842,19949,19121,18386,17651,16998,16349,15800,15247, |
1040 | 21.2k | 14783,14321,13859,13535,13081,12702,12423,12056,11779,11513, |
1041 | 21.2k | 11135,10955,10676,10392,10208, 9928, 9747, 9564, 9369, 9193, |
1042 | 21.2k | 9017, 8822, 8639, 8458, 8270, 8084, 7896, 7710, 7527, 7347, |
1043 | 21.2k | 7156, 6977, 6788, 6607, 6422, 6236, 6054, 5867, 5684, 5495, |
1044 | 21.2k | 5305, 5128, 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, |
1045 | 21.2k | 3509, 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846, |
1046 | 21.2k | 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201, 128, |
1047 | 21.2k | 0 |
1048 | 21.2k | }; |
1049 | | |
1050 | 21.2k | hashval=(jpeg_info->quant_tbl_ptrs[0]->quantval[2]+ |
1051 | 21.2k | jpeg_info->quant_tbl_ptrs[0]->quantval[53]+ |
1052 | 21.2k | jpeg_info->quant_tbl_ptrs[1]->quantval[0]+ |
1053 | 21.2k | jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]); |
1054 | 858k | for (i=0; i < 100; i++) |
1055 | 858k | { |
1056 | 858k | if ((hashval >= hash[i]) || (sum >= sums[i])) |
1057 | 21.1k | { |
1058 | 21.1k | save_quality=i+1; |
1059 | 21.1k | if (image->logging) |
1060 | 21.1k | { |
1061 | 21.1k | if ((hashval > hash[i]) || (sum > sums[i])) |
1062 | 18.5k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1063 | 18.5k | "Quality: %d (approximate)", |
1064 | 18.5k | save_quality); |
1065 | 2.54k | else |
1066 | 2.54k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1067 | 2.54k | "Quality: %d",save_quality); |
1068 | 21.1k | } |
1069 | 21.1k | break; |
1070 | 21.1k | } |
1071 | 858k | } |
1072 | 21.2k | } |
1073 | 149k | else |
1074 | 149k | if (jpeg_info->quant_tbl_ptrs[0] != NULL) |
1075 | 62.1k | { |
1076 | 62.1k | int |
1077 | 62.1k | bwhash[] = |
1078 | 62.1k | { |
1079 | 62.1k | 510, 505, 422, 380, 355, 338, 326, 318, 311, 305, |
1080 | 62.1k | 300, 297, 293, 291, 288, 286, 284, 283, 281, 280, |
1081 | 62.1k | 279, 278, 277, 273, 262, 251, 243, 233, 225, 218, |
1082 | 62.1k | 211, 205, 198, 193, 186, 181, 177, 172, 168, 164, |
1083 | 62.1k | 158, 156, 152, 148, 145, 142, 139, 136, 133, 131, |
1084 | 62.1k | 129, 126, 123, 120, 118, 115, 113, 110, 107, 105, |
1085 | 62.1k | 102, 100, 97, 94, 92, 89, 87, 83, 81, 79, |
1086 | 62.1k | 76, 74, 70, 68, 66, 63, 61, 57, 55, 52, |
1087 | 62.1k | 50, 48, 44, 42, 39, 37, 34, 31, 29, 26, |
1088 | 62.1k | 24, 21, 18, 16, 13, 11, 8, 6, 3, 2, |
1089 | 62.1k | 0 |
1090 | 62.1k | }; |
1091 | | |
1092 | 62.1k | int |
1093 | 62.1k | bwsum[] = |
1094 | 62.1k | { |
1095 | 62.1k | 16320,16315,15946,15277,14655,14073,13623,13230,12859,12560, |
1096 | 62.1k | 12240,11861,11456,11081,10714,10360,10027, 9679, 9368, 9056, |
1097 | 62.1k | 8680, 8331, 7995, 7668, 7376, 7084, 6823, 6562, 6345, 6125, |
1098 | 62.1k | 5939, 5756, 5571, 5421, 5240, 5086, 4976, 4829, 4719, 4616, |
1099 | 62.1k | 4463, 4393, 4280, 4166, 4092, 3980, 3909, 3835, 3755, 3688, |
1100 | 62.1k | 3621, 3541, 3467, 3396, 3323, 3247, 3170, 3096, 3021, 2952, |
1101 | 62.1k | 2874, 2804, 2727, 2657, 2583, 2509, 2437, 2362, 2290, 2211, |
1102 | 62.1k | 2136, 2068, 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, |
1103 | 62.1k | 1398, 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736, |
1104 | 62.1k | 667, 592, 518, 441, 369, 292, 221, 151, 86, 64, |
1105 | 62.1k | 0 |
1106 | 62.1k | }; |
1107 | | |
1108 | 62.1k | hashval=(jpeg_info->quant_tbl_ptrs[0]->quantval[2]+ |
1109 | 62.1k | jpeg_info->quant_tbl_ptrs[0]->quantval[53]); |
1110 | 1.89M | for (i=0; i < 100; i++) |
1111 | 1.89M | { |
1112 | 1.89M | if ((hashval >= bwhash[i]) || (sum >= bwsum[i])) |
1113 | 61.9k | { |
1114 | 61.9k | save_quality=i+1; |
1115 | 61.9k | if (image->logging) |
1116 | 61.9k | { |
1117 | 61.9k | if ((hashval > bwhash[i]) || (sum > bwsum[i])) |
1118 | 59.2k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1119 | 59.2k | "Quality: %ld (approximate)", |
1120 | 59.2k | i+1); |
1121 | 2.76k | else |
1122 | 2.76k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1123 | 2.76k | "Quality: %ld",i+1); |
1124 | 61.9k | } |
1125 | 61.9k | break; |
1126 | 61.9k | } |
1127 | 1.89M | } |
1128 | 62.1k | } |
1129 | 171k | } |
1130 | | |
1131 | 171k | return save_quality; |
1132 | 171k | } |
1133 | | |
1134 | | static const char *JPEGColorSpaceToString(const J_COLOR_SPACE colorspace) |
1135 | 171k | { |
1136 | 171k | const char |
1137 | 171k | *s; |
1138 | | |
1139 | 171k | switch (colorspace) |
1140 | 171k | { |
1141 | 0 | default: |
1142 | 3.16k | case JCS_UNKNOWN: |
1143 | 3.16k | s = "UNKNOWN"; |
1144 | 3.16k | break; |
1145 | 48.3k | case JCS_GRAYSCALE: |
1146 | 48.3k | s = "GRAYSCALE"; |
1147 | 48.3k | break; |
1148 | 91.4k | case JCS_RGB: |
1149 | 91.4k | s = "RGB"; |
1150 | 91.4k | break; |
1151 | 0 | case JCS_YCbCr: |
1152 | 0 | s = "YCbCr"; |
1153 | 0 | break; |
1154 | 28.2k | case JCS_CMYK: |
1155 | 28.2k | s = "CMYK"; |
1156 | 28.2k | break; |
1157 | 0 | case JCS_YCCK: |
1158 | 0 | s = "YCCK"; |
1159 | 0 | break; |
1160 | 171k | } |
1161 | 171k | return s; |
1162 | 171k | } |
1163 | | |
1164 | | /* |
1165 | | Format JPEG color space to a string buffer of length MaxTextExtent. |
1166 | | */ |
1167 | | static void |
1168 | | FormatJPEGColorSpace(const J_COLOR_SPACE colorspace, |
1169 | | char *colorspace_name) |
1170 | 171k | { |
1171 | 171k | (void) strlcpy(colorspace_name,JPEGColorSpaceToString(colorspace),MaxTextExtent); |
1172 | 171k | } |
1173 | | |
1174 | | /* |
1175 | | Format JPEG sampling factors to a string. |
1176 | | */ |
1177 | | static MagickPassFail |
1178 | | FormatJPEGSamplingFactors(const struct jpeg_decompress_struct *jpeg_info, |
1179 | | char *sampling_factors) |
1180 | 171k | { |
1181 | 171k | unsigned int |
1182 | 171k | quantums = 0; |
1183 | | |
1184 | 171k | MagickPassFail |
1185 | 171k | status = MagickFail; |
1186 | | |
1187 | 171k | switch (jpeg_info->out_color_space) |
1188 | 171k | { |
1189 | 0 | default: |
1190 | 3.16k | case JCS_UNKNOWN: |
1191 | | /* error/unspecified */ |
1192 | 3.16k | break; |
1193 | 48.3k | case JCS_GRAYSCALE: |
1194 | | /* monochrome */ |
1195 | 48.3k | quantums = 1; |
1196 | 48.3k | break; |
1197 | 91.4k | case JCS_RGB: |
1198 | | /* red/green/blue as specified by the RGB_RED, RGB_GREEN, |
1199 | | RGB_BLUE, and RGB_PIXELSIZE macros */ |
1200 | 91.4k | quantums = 3; |
1201 | 91.4k | break; |
1202 | 0 | case JCS_YCbCr: |
1203 | | /* Y/Cb/Cr (also known as YUV) */ |
1204 | 0 | quantums = 3; |
1205 | 0 | break; |
1206 | 28.2k | case JCS_CMYK: |
1207 | | /* C/M/Y/K */ |
1208 | 28.2k | quantums = 4; |
1209 | 28.2k | break; |
1210 | 0 | case JCS_YCCK: |
1211 | | /* Y/Cb/Cr/K */ |
1212 | 0 | quantums = 4; |
1213 | 0 | break; |
1214 | | #if 0 |
1215 | | #if defined(JCS_EXTENSIONS) && JCS_EXTENSIONS |
1216 | | case JCS_EXT_RGB: |
1217 | | /* red/green/blue */ |
1218 | | quantums = 3; |
1219 | | break; |
1220 | | case JCS_EXT_RGBX: |
1221 | | /* red/green/blue/x */ |
1222 | | quantums = 4; |
1223 | | break; |
1224 | | case JCS_EXT_BGR: |
1225 | | /* blue/green/red */ |
1226 | | quantums = 3; |
1227 | | break; |
1228 | | case JCS_EXT_BGRX: |
1229 | | /* blue/green/red/x */ |
1230 | | quantums = 4; |
1231 | | break; |
1232 | | case JCS_EXT_XBGR: |
1233 | | /* x/blue/green/red */ |
1234 | | quantums = 4; |
1235 | | break; |
1236 | | case JCS_EXT_XRGB: |
1237 | | /* x/red/green/blue */ |
1238 | | quantums = 4; |
1239 | | break; |
1240 | | |
1241 | | #if defined(JCS_ALPHA_EXTENSIONS) && JCS_ALPHA_EXTENSIONS |
1242 | | case JCS_EXT_RGBA: |
1243 | | /* red/green/blue/alpha */ |
1244 | | quantums = 4; |
1245 | | break; |
1246 | | case JCS_EXT_BGRA: |
1247 | | /* blue/green/red/alpha */ |
1248 | | quantums = 4; |
1249 | | break; |
1250 | | case JCS_EXT_ABGR: |
1251 | | /* alpha/blue/green/red */ |
1252 | | quantums = 4; |
1253 | | break; |
1254 | | case JCS_EXT_ARGB: |
1255 | | /* alpha/red/green/blue */ |
1256 | | quantums = 4; |
1257 | | break; |
1258 | | case JCS_RGB565: |
1259 | | /* 5-bit red/6-bit green/5-bit blue [decompression only] */ |
1260 | | quantums = 3; |
1261 | | break; |
1262 | | #endif /* defined(JCS_ALPHA_EXTENSIONS) && JCS_ALPHA_EXTENSIONS */ |
1263 | | #endif /* if defined(JCS_EXTENSIONS) && JCS_EXTENSIONS */ |
1264 | | #endif |
1265 | 171k | } |
1266 | | |
1267 | 171k | switch (quantums) |
1268 | 171k | { |
1269 | 3.16k | case 0: |
1270 | 3.16k | break; |
1271 | 48.3k | case 1: |
1272 | 48.3k | (void) FormatString(sampling_factors,"%dx%d", |
1273 | 48.3k | jpeg_info->comp_info[0].h_samp_factor, |
1274 | 48.3k | jpeg_info->comp_info[0].v_samp_factor); |
1275 | 48.3k | status = MagickPass; |
1276 | 48.3k | break; |
1277 | 91.4k | case 3: |
1278 | 91.4k | (void) FormatString(sampling_factors,"%dx%d,%dx%d,%dx%d", |
1279 | 91.4k | jpeg_info->comp_info[0].h_samp_factor, |
1280 | 91.4k | jpeg_info->comp_info[0].v_samp_factor, |
1281 | 91.4k | jpeg_info->comp_info[1].h_samp_factor, |
1282 | 91.4k | jpeg_info->comp_info[1].v_samp_factor, |
1283 | 91.4k | jpeg_info->comp_info[2].h_samp_factor, |
1284 | 91.4k | jpeg_info->comp_info[2].v_samp_factor); |
1285 | 91.4k | status = MagickPass; |
1286 | 91.4k | break; |
1287 | 28.2k | case 4: |
1288 | 28.2k | (void) FormatString(sampling_factors,"%dx%d,%dx%d,%dx%d,%dx%d", |
1289 | 28.2k | jpeg_info->comp_info[0].h_samp_factor, |
1290 | 28.2k | jpeg_info->comp_info[0].v_samp_factor, |
1291 | 28.2k | jpeg_info->comp_info[1].h_samp_factor, |
1292 | 28.2k | jpeg_info->comp_info[1].v_samp_factor, |
1293 | 28.2k | jpeg_info->comp_info[2].h_samp_factor, |
1294 | 28.2k | jpeg_info->comp_info[2].v_samp_factor, |
1295 | 28.2k | jpeg_info->comp_info[3].h_samp_factor, |
1296 | 28.2k | jpeg_info->comp_info[3].v_samp_factor); |
1297 | 28.2k | status = MagickPass; |
1298 | 28.2k | break; |
1299 | 171k | } |
1300 | 171k | return status; |
1301 | 171k | } |
1302 | | |
1303 | | static MagickBool |
1304 | | IsITUFax(const Image* image) |
1305 | 332k | { |
1306 | 332k | size_t |
1307 | 332k | profile_length; |
1308 | | |
1309 | 332k | const unsigned char |
1310 | 332k | *profile; |
1311 | | |
1312 | 332k | MagickBool |
1313 | 332k | status; |
1314 | | |
1315 | 332k | status=MagickFalse; |
1316 | 332k | if ((profile=GetImageProfile(image,"APP1",&profile_length)) && |
1317 | 0 | (profile_length >= 5)) |
1318 | 0 | { |
1319 | 0 | if (profile[0] == 0x47 && |
1320 | 0 | profile[1] == 0x33 && |
1321 | 0 | profile[2] == 0x46 && |
1322 | 0 | profile[3] == 0x41 && |
1323 | 0 | profile[4] == 0x58) |
1324 | 0 | status=MagickTrue; |
1325 | 0 | } |
1326 | | |
1327 | 332k | return status; |
1328 | 332k | } |
1329 | | |
1330 | | #define ThrowJPEGReaderException(code_,reason_,image_) \ |
1331 | 76.2k | { \ |
1332 | 76.2k | client_data=FreeMagickClientData(client_data); \ |
1333 | 76.2k | ThrowReaderException(code_,reason_,image_); \ |
1334 | 0 | } |
1335 | | |
1336 | | static Image *ReadJPEGImage(const ImageInfo *image_info, |
1337 | | ExceptionInfo *exception) |
1338 | 706k | { |
1339 | 706k | Image |
1340 | 706k | *image; |
1341 | | |
1342 | 706k | MagickClientData |
1343 | 706k | *client_data = (MagickClientData *) NULL; |
1344 | | |
1345 | 706k | IndexPacket |
1346 | 706k | index; |
1347 | | |
1348 | 706k | long |
1349 | 706k | y; |
1350 | | |
1351 | 706k | magick_jpeg_pixels_t |
1352 | 706k | jpeg_pixels; /* Contents freed by FreeMagickClientData() */ |
1353 | | |
1354 | 706k | const char |
1355 | 706k | *value; |
1356 | | |
1357 | 706k | register unsigned long |
1358 | 706k | i; |
1359 | | |
1360 | 706k | struct jpeg_error_mgr |
1361 | 706k | jpeg_error; |
1362 | | |
1363 | 706k | struct jpeg_progress_mgr |
1364 | 706k | jpeg_progress; |
1365 | | |
1366 | 706k | struct jpeg_decompress_struct |
1367 | 706k | jpeg_info; |
1368 | | |
1369 | 706k | MagickPassFail |
1370 | 706k | status; |
1371 | | |
1372 | 706k | unsigned long |
1373 | 706k | number_pixels; |
1374 | | |
1375 | | /* |
1376 | | Open image file. |
1377 | | */ |
1378 | 706k | assert(image_info != (const ImageInfo *) NULL); |
1379 | 706k | assert(image_info->signature == MagickSignature); |
1380 | 706k | assert(exception != (ExceptionInfo *) NULL); |
1381 | 706k | assert(exception->signature == MagickSignature); |
1382 | 706k | image=AllocateImage(image_info); |
1383 | 706k | if (image == (Image *) NULL) |
1384 | 706k | ThrowJPEGReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1385 | 706k | client_data=AllocateMagickClientData(); |
1386 | 706k | if (client_data == (MagickClientData *) NULL) |
1387 | 706k | ThrowJPEGReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1388 | 706k | status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception); |
1389 | 706k | if (status == MagickFail) |
1390 | 706k | ThrowJPEGReaderException(FileOpenError,UnableToOpenFile,image); |
1391 | 706k | if (BlobIsSeekable(image) && GetBlobSize(image) < 107) |
1392 | 681k | ThrowJPEGReaderException(CorruptImageError,InsufficientImageDataInFile,image); |
1393 | | /* |
1394 | | Initialize structures. |
1395 | | */ |
1396 | 681k | (void) memset(&jpeg_pixels,0,sizeof(jpeg_pixels)); |
1397 | 681k | (void) memset(&jpeg_progress,0,sizeof(jpeg_progress)); |
1398 | 681k | (void) memset(&jpeg_info,0,sizeof(jpeg_info)); |
1399 | 681k | (void) memset(&jpeg_error,0,sizeof(jpeg_error)); |
1400 | 681k | jpeg_info.err=jpeg_std_error(&jpeg_error); |
1401 | 681k | jpeg_info.err->emit_message=/*(void (*)(j_common_ptr,int))*/ JPEGDecodeMessageHandler; |
1402 | 681k | jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; |
1403 | 681k | client_data->image=image; |
1404 | 681k | client_data->ping=image_info->ping; |
1405 | 681k | client_data->max_scan_number=100; |
1406 | 681k | client_data->max_warning_count=MaxWarningCount; |
1407 | 681k | client_data->jpeg_pixels=&jpeg_pixels; |
1408 | | |
1409 | | /* |
1410 | | Allow the user to set how many warnings of any given type are |
1411 | | allowed before promotion of the warning to a hard error. |
1412 | | */ |
1413 | 681k | if ((value=AccessDefinition(image_info,"jpeg","max-warnings"))) |
1414 | 0 | client_data->max_warning_count=strtol(value,(char **) NULL, 10); |
1415 | | |
1416 | | /* |
1417 | | Set initial longjmp based error handler. |
1418 | | */ |
1419 | 681k | if (setjmp(client_data->error_recovery) != 0) |
1420 | 463k | { |
1421 | 463k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1422 | 463k | "Setjmp return from longjmp!"); |
1423 | 463k | jpeg_destroy_decompress(&jpeg_info); |
1424 | 463k | GetImageException(image,exception); |
1425 | 463k | client_data=FreeMagickClientData(client_data); |
1426 | 463k | CloseBlob(image); |
1427 | 463k | if (exception->severity < ErrorException) |
1428 | 0 | return(image); |
1429 | 463k | DestroyImage(image); |
1430 | 463k | return((Image *) NULL); |
1431 | 463k | } |
1432 | 218k | jpeg_info.client_data=(void *) client_data; |
1433 | | |
1434 | 218k | jpeg_create_decompress(&jpeg_info); |
1435 | | /* |
1436 | | Specify a memory limit for libjpeg which is 1/5th the absolute |
1437 | | limit. Don't actually consume the resource since we don't know |
1438 | | how much libjpeg will actually consume. |
1439 | | */ |
1440 | 218k | jpeg_info.mem->max_memory_to_use=(long) (GetMagickResourceLimit(MemoryResource) - |
1441 | 218k | GetMagickResource(MemoryResource))/5U; |
1442 | 218k | if (image->logging) |
1443 | 681k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1444 | 681k | "Memory capped to %ld bytes", jpeg_info.mem->max_memory_to_use); |
1445 | | /* |
1446 | | Register our progress monitor |
1447 | | */ |
1448 | 218k | jpeg_progress.progress_monitor=(void (*)(j_common_ptr)) JPEGDecodeProgressMonitor; |
1449 | 218k | jpeg_info.progress=&jpeg_progress; |
1450 | | |
1451 | 218k | JPEGSourceManager(&jpeg_info,image); |
1452 | 218k | jpeg_set_marker_processor(&jpeg_info,JPEG_COM,ReadComment); |
1453 | 218k | jpeg_set_marker_processor(&jpeg_info,ICC_MARKER,ReadICCProfile); |
1454 | 218k | jpeg_set_marker_processor(&jpeg_info,IPTC_MARKER,ReadIPTCProfile); |
1455 | 10.4M | for (i=1; i < 16; i++) |
1456 | 10.2M | if ((i != 2) && (i != 13) && (i != 14)) |
1457 | 8.17M | jpeg_set_marker_processor(&jpeg_info,JPEG_APP0+i,ReadGenericProfile); |
1458 | 218k | if (image->logging) |
1459 | 681k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1460 | 681k | "Reading JPEG header..."); |
1461 | 218k | i=jpeg_read_header(&jpeg_info,True); |
1462 | 218k | if (image->logging) |
1463 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1464 | 332k | "Done with reading JPEG header"); |
1465 | 218k | if (IsITUFax(image)) |
1466 | 0 | { |
1467 | 0 | if (image->logging) |
1468 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1469 | 0 | "Image colorspace set to LAB"); |
1470 | 0 | image->colorspace=LABColorspace; |
1471 | 0 | jpeg_info.out_color_space = JCS_YCbCr; |
1472 | 0 | } |
1473 | 218k | else if (jpeg_info.out_color_space == JCS_CMYK) |
1474 | 67.1k | { |
1475 | 67.1k | if (image->logging) |
1476 | 67.1k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1477 | 67.1k | "Image colorspace set to CMYK"); |
1478 | 67.1k | image->colorspace=CMYKColorspace; |
1479 | 67.1k | } |
1480 | 218k | if (jpeg_info.saw_JFIF_marker) |
1481 | 51.8k | { |
1482 | 51.8k | if ((jpeg_info.X_density != 1U) && (jpeg_info.Y_density != 1U)) |
1483 | 50.7k | { |
1484 | | /* |
1485 | | Set image resolution. |
1486 | | */ |
1487 | 50.7k | image->x_resolution=jpeg_info.X_density; |
1488 | 50.7k | image->y_resolution=jpeg_info.Y_density; |
1489 | 50.7k | if (jpeg_info.density_unit == 1) |
1490 | 7.22k | image->units=PixelsPerInchResolution; |
1491 | 50.7k | if (jpeg_info.density_unit == 2) |
1492 | 735 | image->units=PixelsPerCentimeterResolution; |
1493 | 50.7k | if (image->logging) |
1494 | 50.7k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1495 | 50.7k | "Image resolution set to %gx%g %s", |
1496 | 50.7k | image->x_resolution, |
1497 | 50.7k | image->y_resolution, |
1498 | 50.7k | ResolutionTypeToString(image->units)); |
1499 | 50.7k | } |
1500 | 51.8k | } |
1501 | | |
1502 | | /* |
1503 | | If the desired image size is pre-set (e.g. by using -size), then |
1504 | | let the JPEG library subsample for us. |
1505 | | */ |
1506 | 218k | number_pixels=image->columns*image->rows; |
1507 | 218k | if (number_pixels != 0) |
1508 | 0 | { |
1509 | 0 | double |
1510 | 0 | scale_factor; |
1511 | | |
1512 | |
|
1513 | 0 | if (image->logging) |
1514 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1515 | 0 | "Requested Geometry: %lux%lu", |
1516 | 0 | image->columns,image->rows); |
1517 | 0 | jpeg_calc_output_dimensions(&jpeg_info); |
1518 | 0 | image->magick_columns=jpeg_info.output_width; |
1519 | 0 | image->magick_rows=jpeg_info.output_height; |
1520 | 0 | if (image->logging) |
1521 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1522 | 0 | "magick_geometry=%lux%lu", |
1523 | 0 | image->magick_columns, image->magick_rows); |
1524 | 0 | scale_factor=(double) jpeg_info.output_width/image->columns; |
1525 | 0 | if (scale_factor > ((double) jpeg_info.output_height/image->rows)) |
1526 | 0 | scale_factor=(double) jpeg_info.output_height/image->rows; |
1527 | 0 | jpeg_info.scale_denom *=(unsigned int) scale_factor; |
1528 | 0 | jpeg_calc_output_dimensions(&jpeg_info); |
1529 | 0 | if (image->logging) |
1530 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1531 | 0 | "Original Geometry: %lux%lu," |
1532 | 0 | " Scale_factor: %ld (scale_num=%d," |
1533 | 0 | " scale_denom=%d)", |
1534 | 0 | image->magick_columns, image->magick_rows, |
1535 | 0 | (long) scale_factor, |
1536 | 0 | jpeg_info.scale_num,jpeg_info.scale_denom); |
1537 | 0 | } |
1538 | | #if 0 |
1539 | | /* |
1540 | | The subrange parameter is set by the filename array syntax similar |
1541 | | to the way an image is requested from a list (e.g. myfile.jpg[2]). |
1542 | | Argument values other than zero are used to scale the image down |
1543 | | by that factor. IJG JPEG 62 (6b) supports values of 1,2,4, or 8 |
1544 | | while IJG JPEG 70 supports all values in the range 1-16. This |
1545 | | feature is useful in case you want to view all of the images with |
1546 | | a consistent ratio. Unfortunately, it uses the same syntax as |
1547 | | list member access. |
1548 | | */ |
1549 | | else if (image_info->subrange != 0) |
1550 | | { |
1551 | | jpeg_info.scale_denom *=(int) image_info->subrange; |
1552 | | jpeg_calc_output_dimensions(&jpeg_info); |
1553 | | if (image->logging) |
1554 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1555 | | "Requested Scaling Denominator: %d " |
1556 | | "(scale_num=%d, scale_denom=%d)", |
1557 | | (int) image_info->subrange, |
1558 | | jpeg_info.scale_num,jpeg_info.scale_denom); |
1559 | | |
1560 | | } |
1561 | | #endif |
1562 | 218k | #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED) |
1563 | | #if !defined(LIBJPEG_TURBO_VERSION_NUMBER) && defined(D_LOSSLESS_SUPPORTED) |
1564 | | /* This code is based on a patch to IJG JPEG 6b, or somesuch. Standard |
1565 | | library does not have a 'process' member. */ |
1566 | | image->interlace= |
1567 | | jpeg_info.process == JPROC_PROGRESSIVE ? LineInterlace : NoInterlace; |
1568 | | image->compression=jpeg_info.process == JPROC_LOSSLESS ? |
1569 | | LosslessJPEGCompression : JPEGCompression; |
1570 | | if (jpeg_info.data_precision > 8) |
1571 | | MagickError2(OptionError, |
1572 | | "12-bit JPEG not supported. Reducing pixel data to 8 bits", |
1573 | | (char *) NULL); |
1574 | | #else |
1575 | 218k | image->interlace=jpeg_info.progressive_mode ? LineInterlace : NoInterlace; |
1576 | 218k | image->compression=JPEGCompression; |
1577 | 218k | #endif |
1578 | | #else |
1579 | | image->compression=JPEGCompression; |
1580 | | image->interlace=LineInterlace; |
1581 | | #endif |
1582 | | |
1583 | | /* |
1584 | | Allow the user to enable/disable block smoothing. |
1585 | | */ |
1586 | 218k | if ((value=AccessDefinition(image_info,"jpeg","block-smoothing"))) |
1587 | 0 | { |
1588 | 0 | if (LocaleCompare(value,"FALSE") == 0) |
1589 | 0 | jpeg_info.do_block_smoothing=False; |
1590 | 0 | else |
1591 | 0 | jpeg_info.do_block_smoothing=True; |
1592 | 0 | } |
1593 | | |
1594 | | /* |
1595 | | Allow the user to select the DCT decoding algorithm. |
1596 | | */ |
1597 | 218k | if ((value=AccessDefinition(image_info,"jpeg","dct-method"))) |
1598 | 0 | { |
1599 | 0 | if (LocaleCompare(value,"ISLOW") == 0) |
1600 | 0 | jpeg_info.dct_method=JDCT_ISLOW; |
1601 | 0 | else if (LocaleCompare(value,"IFAST") == 0) |
1602 | 0 | jpeg_info.dct_method=JDCT_IFAST; |
1603 | 0 | else if (LocaleCompare(value,"FLOAT") == 0) |
1604 | 0 | jpeg_info.dct_method=JDCT_FLOAT; |
1605 | 0 | else if (LocaleCompare(value,"DEFAULT") == 0) |
1606 | 0 | jpeg_info.dct_method=JDCT_DEFAULT; |
1607 | 0 | else if (LocaleCompare(value,"FASTEST") == 0) |
1608 | 0 | jpeg_info.dct_method=JDCT_FASTEST; |
1609 | 0 | } |
1610 | | |
1611 | | /* |
1612 | | Allow the user to enable/disable fancy upsampling. |
1613 | | */ |
1614 | 218k | if ((value=AccessDefinition(image_info,"jpeg","fancy-upsampling"))) |
1615 | 0 | { |
1616 | 0 | if (LocaleCompare(value,"FALSE") == 0) |
1617 | 0 | jpeg_info.do_fancy_upsampling=False; |
1618 | 0 | else |
1619 | 0 | jpeg_info.do_fancy_upsampling=True; |
1620 | 0 | } |
1621 | | |
1622 | | /* |
1623 | | Allow the user to adjust the maximum JPEG scan number |
1624 | | */ |
1625 | 218k | if ((value=AccessDefinition(image_info,"jpeg","max-scan-number"))) |
1626 | 244k | { |
1627 | 244k | client_data->max_scan_number=strtol(value,(char **) NULL, 10); |
1628 | 244k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1629 | 244k | "JPEG max-scan-number set to %d", |
1630 | 244k | client_data->max_scan_number); |
1631 | 244k | } |
1632 | | |
1633 | 218k | jpeg_calc_output_dimensions(&jpeg_info); |
1634 | 218k | image->columns=jpeg_info.output_width; |
1635 | 218k | image->rows=jpeg_info.output_height; |
1636 | 218k | image->depth=Min(jpeg_info.data_precision,Min(16,QuantumDepth)); |
1637 | | |
1638 | 218k | if (image->logging) |
1639 | 332k | { |
1640 | 332k | if (image->interlace == LineInterlace) |
1641 | 147k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1642 | 147k | "Interlace: progressive"); |
1643 | 185k | else |
1644 | 185k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1645 | 185k | "Interlace: nonprogressive"); |
1646 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d", |
1647 | 332k | (int) jpeg_info.data_precision); |
1648 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Components: %d", |
1649 | 332k | (int) jpeg_info.output_components); |
1650 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d", |
1651 | 332k | (int) jpeg_info.output_width, |
1652 | 332k | (int) jpeg_info.output_height); |
1653 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"DCT Method: %d", |
1654 | 332k | jpeg_info.dct_method); |
1655 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Fancy Upsampling: %s", |
1656 | 332k | (jpeg_info.do_fancy_upsampling ? "true" : "false")); |
1657 | 332k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Block Smoothing: %s", |
1658 | 332k | (jpeg_info.do_block_smoothing ? "true" : "false")); |
1659 | 332k | } |
1660 | | |
1661 | 218k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
1662 | 47.1k | { |
1663 | 47.1k | jpeg_destroy_decompress(&jpeg_info); |
1664 | 47.1k | ThrowJPEGReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
1665 | 0 | } |
1666 | | |
1667 | 171k | if (image->logging) |
1668 | 285k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1669 | 285k | "Starting JPEG decompression..."); |
1670 | 171k | (void) jpeg_start_decompress(&jpeg_info); |
1671 | 171k | image->columns=jpeg_info.output_width; |
1672 | 171k | image->rows=jpeg_info.output_height; |
1673 | 171k | { |
1674 | 171k | char |
1675 | 171k | attribute[MaxTextExtent]; |
1676 | | |
1677 | | /* |
1678 | | Estimate and retain JPEG properties as attributes. |
1679 | | */ |
1680 | 171k | FormatString(attribute,"%d",EstimateJPEGQuality(&jpeg_info,image)); |
1681 | 171k | (void) SetImageAttribute(image,"JPEG-Quality",attribute); |
1682 | | |
1683 | 171k | FormatString(attribute,"%ld",(long)jpeg_info.out_color_space); |
1684 | 171k | (void) SetImageAttribute(image,"JPEG-Colorspace",attribute); |
1685 | | |
1686 | 171k | FormatJPEGColorSpace(jpeg_info.out_color_space,attribute); |
1687 | 171k | (void) SetImageAttribute(image,"JPEG-Colorspace-Name",attribute); |
1688 | 171k | if (image->logging) |
1689 | 171k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1690 | 171k | "Colorspace: %s (%d)", attribute, |
1691 | 171k | jpeg_info.out_color_space); |
1692 | | |
1693 | 171k | if (FormatJPEGSamplingFactors(&jpeg_info,attribute) != MagickFail) |
1694 | 168k | { |
1695 | 168k | (void) SetImageAttribute(image,"JPEG-Sampling-factors",attribute); |
1696 | 168k | if (image->logging) |
1697 | 168k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1698 | 168k | "Sampling Factors: %s", attribute); |
1699 | 168k | } |
1700 | 171k | } |
1701 | | |
1702 | 171k | image->depth=Min(jpeg_info.data_precision,Min(16,QuantumDepth)); |
1703 | 171k | if (jpeg_info.out_color_space == JCS_GRAYSCALE) |
1704 | 48.3k | { |
1705 | | /* |
1706 | | Build colormap if we can |
1707 | | */ |
1708 | 48.3k | unsigned long max_index = MaxValueGivenBits(image->depth); |
1709 | 48.3k | if (max_index <= MaxMap) |
1710 | 48.3k | if (!AllocateImageColormap(image,max_index+1LU)) |
1711 | 0 | { |
1712 | 0 | jpeg_destroy_decompress(&jpeg_info); |
1713 | 0 | ThrowJPEGReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1714 | 0 | } |
1715 | 48.3k | if (image_info->ping) |
1716 | 0 | { |
1717 | 0 | image->is_grayscale=MagickTrue; |
1718 | 0 | } |
1719 | 48.3k | } |
1720 | 122k | else |
1721 | 122k | { |
1722 | 122k | if (image_info->ping) |
1723 | 0 | { |
1724 | 0 | image->is_grayscale=MagickFalse; |
1725 | 0 | image->is_monochrome=MagickFalse; |
1726 | 0 | } |
1727 | 122k | } |
1728 | | |
1729 | | /* |
1730 | | Store profiles in image. |
1731 | | */ |
1732 | 2.91M | for (i=0 ; i < ArraySize(client_data->profiles); i++) |
1733 | 2.73M | { |
1734 | 2.73M | ProfileInfo *profile=&client_data->profiles[i]; |
1735 | 2.73M | if (!profile->name) |
1736 | 2.72M | continue; |
1737 | 18.9k | if (!profile->length) |
1738 | 0 | continue; |
1739 | 18.9k | if (!profile->info) |
1740 | 0 | continue; |
1741 | 18.9k | (void) SetImageProfile(image,profile->name,profile->info,profile->length); |
1742 | 18.9k | } |
1743 | | |
1744 | 171k | if (image_info->ping) |
1745 | 0 | { |
1746 | 0 | jpeg_destroy_decompress(&jpeg_info); |
1747 | 0 | client_data=FreeMagickClientData(client_data); |
1748 | 0 | CloseBlob(image); |
1749 | 0 | return(image); |
1750 | 0 | } |
1751 | 171k | if (CheckImagePixelLimits(image, exception) != MagickPass) |
1752 | 0 | { |
1753 | 0 | jpeg_destroy_decompress(&jpeg_info); |
1754 | 0 | ThrowJPEGReaderException(ResourceLimitError,ImagePixelLimitExceeded,image); |
1755 | 0 | } |
1756 | | |
1757 | | /* |
1758 | | Verify that we support the number of output components. |
1759 | | */ |
1760 | 171k | if ((jpeg_info.output_components != 1) && |
1761 | 122k | (jpeg_info.output_components != 3) && |
1762 | 31.4k | (jpeg_info.output_components != 4)) |
1763 | 3.16k | { |
1764 | 3.16k | jpeg_destroy_decompress(&jpeg_info); |
1765 | 3.16k | ThrowJPEGReaderException(CoderError,ImageTypeNotSupported,image); |
1766 | 0 | } |
1767 | | /* |
1768 | | Verify that file size is reasonable (if we can) |
1769 | | */ |
1770 | 168k | if (BlobIsSeekable(image)) |
1771 | 168k | { |
1772 | 168k | magick_off_t |
1773 | 168k | blob_size; |
1774 | | |
1775 | 168k | double |
1776 | 168k | ratio = 0; |
1777 | | |
1778 | 168k | blob_size = GetBlobSize(image); |
1779 | | |
1780 | 168k | if (blob_size != 0) |
1781 | 168k | { |
1782 | | /* magick columns/rows are only set if size was specified! */ |
1783 | 168k | if (image->magick_columns && image->magick_rows) |
1784 | 0 | ratio = ((double) image->magick_columns*image->magick_rows* |
1785 | 0 | jpeg_info.output_components/blob_size); |
1786 | 168k | else |
1787 | 168k | ratio = ((double) image->columns*image->rows* |
1788 | 168k | jpeg_info.output_components/blob_size); |
1789 | 168k | } |
1790 | | |
1791 | | /* All-black JPEG can produce tremendous compression ratios. |
1792 | | Allow for it. */ |
1793 | 168k | if ((blob_size == 0) || (ratio > 2500.0)) |
1794 | 1.10k | { |
1795 | 1.10k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1796 | 1.10k | "Unreasonable dimensions: " |
1797 | 1.10k | "geometry=%lux%lu," |
1798 | 1.10k | " magick_geometry=%lux%lu," |
1799 | 1.10k | " components=%d, " |
1800 | 1.10k | "blob size=%" MAGICK_OFF_F "d bytes, " |
1801 | 1.10k | "compression ratio %g", |
1802 | 1.10k | image->columns, image->rows, |
1803 | 1.10k | image->magick_columns, image->magick_rows, |
1804 | 1.10k | jpeg_info.output_components, blob_size, ratio); |
1805 | | |
1806 | 1.10k | jpeg_destroy_decompress(&jpeg_info); |
1807 | 1.10k | ThrowJPEGReaderException(CorruptImageError,InsufficientImageDataInFile,image); |
1808 | 0 | } |
1809 | 168k | } |
1810 | | |
1811 | 166k | switch (jpeg_info.data_precision) |
1812 | 166k | { |
1813 | 0 | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
1814 | 5.25k | case 16: |
1815 | 5.25k | jpeg_pixels.t.j16 = |
1816 | 5.25k | MagickAllocateResourceLimitedClearedArray(J16SAMPLE *, |
1817 | 5.25k | jpeg_info.output_components, |
1818 | 5.25k | MagickArraySize(image->columns, |
1819 | 5.25k | sizeof(J16SAMPLE))); |
1820 | 5.25k | break; |
1821 | 0 | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
1822 | 0 | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
1823 | 37.1k | case 12: |
1824 | 37.1k | jpeg_pixels.t.j12 = |
1825 | 37.1k | MagickAllocateResourceLimitedClearedArray(J12SAMPLE *, |
1826 | 37.1k | jpeg_info.output_components, |
1827 | 37.1k | MagickArraySize(image->columns, |
1828 | 37.1k | sizeof(J12SAMPLE))); |
1829 | 37.1k | break; |
1830 | 0 | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
1831 | 124k | default: |
1832 | 124k | { |
1833 | 124k | jpeg_pixels.t.j = |
1834 | 124k | MagickAllocateResourceLimitedClearedArray(JSAMPLE *, |
1835 | 124k | jpeg_info.output_components, |
1836 | 124k | MagickArraySize(image->columns, |
1837 | 124k | sizeof(JSAMPLE))); |
1838 | 124k | } |
1839 | 166k | } |
1840 | | |
1841 | 166k | if (jpeg_pixels.t.v == (void *) NULL) |
1842 | 0 | { |
1843 | 0 | jpeg_destroy_decompress(&jpeg_info); |
1844 | 0 | ThrowJPEGReaderException(ResourceLimitError,MemoryAllocationFailed,image); |
1845 | 0 | } |
1846 | | |
1847 | | /* |
1848 | | Extended longjmp-based error handler (with jpeg_pixels) |
1849 | | */ |
1850 | 166k | if (setjmp(client_data->error_recovery) != 0) |
1851 | 5.63k | { |
1852 | 5.63k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
1853 | 5.63k | "Setjmp return from longjmp!"); |
1854 | | /* Error handling code executed if longjmp was invoked */ |
1855 | 5.63k | MagickFreeResourceLimitedMemory(void *,jpeg_pixels.t.v); |
1856 | 5.63k | jpeg_destroy_decompress(&jpeg_info); |
1857 | 5.63k | if (image->exception.severity > exception->severity) |
1858 | 1.47k | CopyException(exception,&image->exception); |
1859 | 5.63k | client_data=FreeMagickClientData(client_data); |
1860 | 5.63k | CloseBlob(image); |
1861 | 5.63k | number_pixels=image->columns*image->rows; |
1862 | 5.63k | if (number_pixels != 0) |
1863 | 5.63k | return(image); |
1864 | 0 | DestroyImage(image); |
1865 | 0 | return((Image *) NULL); |
1866 | 5.63k | } |
1867 | | |
1868 | | |
1869 | | /* |
1870 | | Convert JPEG pixels to pixel packets. |
1871 | | */ |
1872 | 12.1M | for (y=0; y < (long) image->rows; y++) |
1873 | 12.0M | { |
1874 | 12.0M | register IndexPacket |
1875 | 12.0M | *indexes; |
1876 | | |
1877 | 12.0M | register long |
1878 | 12.0M | x; |
1879 | | |
1880 | 12.0M | register PixelPacket |
1881 | 12.0M | *q; |
1882 | | |
1883 | | /* |
1884 | | Read scanlines (one scanline per cycle). Stop at first serious error. |
1885 | | */ |
1886 | 12.0M | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
1887 | 12.0M | if (jpeg_info.data_precision == 16) |
1888 | 666k | { |
1889 | 666k | { |
1890 | 666k | J16SAMPROW |
1891 | 666k | scanline[1]; |
1892 | | |
1893 | 666k | scanline[0]=(J16SAMPROW) jpeg_pixels.t.j16; |
1894 | 666k | if ((jpeg16_read_scanlines(&jpeg_info, scanline,1) != 1) || |
1895 | 664k | (image->exception.severity >= ErrorException)) |
1896 | 1 | { |
1897 | 1 | status=MagickFail; |
1898 | 1 | break; |
1899 | 1 | } |
1900 | 666k | } |
1901 | 666k | } else |
1902 | 11.3M | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
1903 | 11.3M | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
1904 | 11.3M | if (jpeg_info.data_precision == 12) |
1905 | 1.20M | { |
1906 | 1.20M | J12SAMPROW |
1907 | 1.20M | scanline[1]; |
1908 | | |
1909 | 1.20M | scanline[0]=(J12SAMPROW) jpeg_pixels.t.j12; |
1910 | 1.20M | if ((jpeg12_read_scanlines(&jpeg_info, scanline,1) != 1) || |
1911 | 1.20M | (image->exception.severity >= ErrorException)) |
1912 | 3.14k | { |
1913 | 3.14k | status=MagickFail; |
1914 | 3.14k | break; |
1915 | 3.14k | } |
1916 | 1.20M | } else |
1917 | 10.1M | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
1918 | 10.1M | { |
1919 | 10.1M | JSAMPROW |
1920 | 10.1M | scanline[1]; |
1921 | | |
1922 | 10.1M | scanline[0]=(JSAMPROW) jpeg_pixels.t.j; |
1923 | 10.1M | if ((jpeg_read_scanlines(&jpeg_info, scanline,1) != 1) || |
1924 | 10.1M | (image->exception.severity >= ErrorException)) |
1925 | 221 | { |
1926 | 221 | status=MagickFail; |
1927 | 221 | break; |
1928 | 221 | } |
1929 | 10.1M | } |
1930 | | |
1931 | 12.0M | q=SetImagePixels(image,0,y,image->columns,1); |
1932 | 12.0M | if (q == (PixelPacket *) NULL) |
1933 | 21 | { |
1934 | 21 | status=MagickFail; |
1935 | 21 | break; |
1936 | 21 | } |
1937 | 12.0M | indexes=AccessMutableIndexes(image); |
1938 | | |
1939 | 12.0M | if (jpeg_info.output_components == 1) |
1940 | 5.35M | { |
1941 | 5.35M | if (image->storage_class == PseudoClass) |
1942 | 5.35M | { |
1943 | 5.35M | switch(jpeg_info.data_precision) |
1944 | 5.35M | { |
1945 | 0 | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
1946 | 64.8k | case 16: |
1947 | 64.8k | { |
1948 | 3.04M | for (x=0; x < (long) image->columns; x++) |
1949 | 2.97M | { |
1950 | 2.97M | index=(IndexPacket) ScaleQuantumToIndex((ScaleShortToQuantum(jpeg_pixels.t.j16[x]))); |
1951 | 2.97M | VerifyColormapIndex(image,index); |
1952 | 2.97M | indexes[x]=index; |
1953 | 2.97M | *q++=image->colormap[index]; |
1954 | 2.97M | } |
1955 | 64.8k | break; |
1956 | 0 | } |
1957 | 0 | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
1958 | 0 | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
1959 | 105k | case 12: |
1960 | 105k | { |
1961 | 105k | const unsigned int |
1962 | 105k | scale_short = 65535U/MAXJ12SAMPLE; |
1963 | | |
1964 | 5.19M | for (x=0; x < (long) image->columns; x++) |
1965 | 5.09M | { |
1966 | 5.09M | index=(IndexPacket) ScaleQuantumToIndex((ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[x])))); |
1967 | 5.09M | VerifyColormapIndex(image,index); |
1968 | 5.09M | indexes[x]=index; |
1969 | 5.09M | *q++=image->colormap[index]; |
1970 | 5.09M | } |
1971 | 105k | break; |
1972 | 0 | } |
1973 | 0 | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
1974 | 5.18M | default: |
1975 | 5.18M | { |
1976 | 1.85G | for (x=0; x < (long) image->columns; x++) |
1977 | 1.84G | { |
1978 | 1.84G | index=(IndexPacket) (GETJSAMPLE(jpeg_pixels.t.j[x])); |
1979 | 1.84G | VerifyColormapIndex(image,index); |
1980 | 1.84G | indexes[x]=index; |
1981 | 1.84G | *q++=image->colormap[index]; |
1982 | 1.84G | } |
1983 | 5.18M | } |
1984 | 5.35M | } |
1985 | 5.35M | } |
1986 | 0 | else |
1987 | 0 | { |
1988 | 0 | switch(jpeg_info.data_precision) |
1989 | 0 | { |
1990 | 0 | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
1991 | 0 | case 16: |
1992 | 0 | { |
1993 | | /* J16SAMPLE is a 'unsigned short' with maximum value MAXJ16SAMPLE (65535) */ |
1994 | 0 | for (x=0; x < (long) image->columns; x++) |
1995 | 0 | { |
1996 | 0 | q->red=q->green=q->blue=ScaleShortToQuantum(jpeg_pixels.t.j16[x]); |
1997 | 0 | q->opacity=OpaqueOpacity; |
1998 | 0 | q++; |
1999 | 0 | } |
2000 | 0 | break; |
2001 | 0 | } |
2002 | 0 | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
2003 | 0 | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
2004 | 0 | case 12: |
2005 | 0 | { |
2006 | | /* J12SAMPLE is a 'short' with maximum value MAXJ12SAMPLE (4095) */ |
2007 | 0 | const unsigned int |
2008 | 0 | scale_short = 65535U/MAXJ12SAMPLE; |
2009 | |
|
2010 | 0 | for (x=0; x < (long) image->columns; x++) |
2011 | 0 | { |
2012 | 0 | q->red=q->green=q->blue=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[x])); |
2013 | 0 | q->opacity=OpaqueOpacity; |
2014 | 0 | q++; |
2015 | 0 | } |
2016 | 0 | break; |
2017 | 0 | } |
2018 | 0 | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
2019 | 0 | default: |
2020 | 0 | { |
2021 | 0 | for (x=0; x < (long) image->columns; x++) |
2022 | 0 | { |
2023 | 0 | q->red=q->green=q->blue=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[x])); |
2024 | 0 | q->opacity=OpaqueOpacity; |
2025 | 0 | q++; |
2026 | 0 | } |
2027 | 0 | } |
2028 | 0 | } |
2029 | 0 | } |
2030 | 5.35M | } |
2031 | 6.67M | else if ((jpeg_info.output_components == 3) || |
2032 | 3.53M | (jpeg_info.output_components == 4)) |
2033 | 6.67M | { |
2034 | 6.67M | switch(jpeg_info.data_precision) |
2035 | 6.67M | { |
2036 | 0 | #if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES |
2037 | 599k | case 16: |
2038 | 599k | { |
2039 | | /* J16SAMPLE is a 'unsigned short' with maximum value MAXJ16SAMPLE (65535) */ |
2040 | 599k | i = 0; |
2041 | 32.8M | for (x=0; x < (long) image->columns; x++) |
2042 | 32.2M | { |
2043 | 32.2M | q->red=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]); |
2044 | 32.2M | q->green=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]); |
2045 | 32.2M | q->blue=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]); |
2046 | 32.2M | if (jpeg_info.output_components > 3) |
2047 | 30.2M | q->opacity=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]); |
2048 | 2.01M | else |
2049 | 2.01M | q->opacity=OpaqueOpacity; |
2050 | 32.2M | q++; |
2051 | 32.2M | } |
2052 | 599k | break; |
2053 | 0 | } |
2054 | 0 | #endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */ |
2055 | 0 | #if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES |
2056 | 1.09M | case 12: |
2057 | 1.09M | { |
2058 | | /* J12SAMPLE is a 'short' with maximum value MAXJ12SAMPLE (4095) */ |
2059 | 1.09M | const unsigned int |
2060 | 1.09M | scale_short = 65535U/MAXJ12SAMPLE; |
2061 | | |
2062 | 1.09M | i = 0; |
2063 | 280M | for (x=0; x < (long) image->columns; x++) |
2064 | 279M | { |
2065 | 279M | q->red=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++])); |
2066 | 279M | q->green=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++])); |
2067 | 279M | q->blue=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++])); |
2068 | 279M | if (jpeg_info.output_components > 3) |
2069 | 79.6M | q->opacity=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++])); |
2070 | 200M | else |
2071 | 200M | q->opacity=OpaqueOpacity; |
2072 | 279M | q++; |
2073 | 279M | } |
2074 | 1.09M | break; |
2075 | 0 | } |
2076 | 0 | #endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */ |
2077 | 4.97M | default: |
2078 | 4.97M | { |
2079 | 4.97M | i = 0; |
2080 | 776M | for (x=0; x < (long) image->columns; x++) |
2081 | 771M | { |
2082 | 771M | q->red=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++])); |
2083 | 771M | q->green=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++])); |
2084 | 771M | q->blue=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++])); |
2085 | 771M | if (jpeg_info.output_components > 3) |
2086 | 384M | q->opacity=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++])); |
2087 | 387M | else |
2088 | 387M | q->opacity=OpaqueOpacity; |
2089 | 771M | q++; |
2090 | 771M | } |
2091 | 4.97M | } |
2092 | 6.67M | } |
2093 | 6.67M | if (image->colorspace == CMYKColorspace) |
2094 | 3.53M | { |
2095 | | /* |
2096 | | CMYK pixels are inverted. |
2097 | | */ |
2098 | 3.53M | q=AccessMutablePixels(image); |
2099 | 498M | for (x=0; x < (long) image->columns; x++) |
2100 | 494M | { |
2101 | 494M | q->red=MaxRGB-q->red; |
2102 | 494M | q->green=MaxRGB-q->green; |
2103 | 494M | q->blue=MaxRGB-q->blue; |
2104 | 494M | q->opacity=MaxRGB-q->opacity; |
2105 | 494M | q++; |
2106 | 494M | } |
2107 | 3.53M | } |
2108 | 6.67M | } |
2109 | 12.0M | if (!SyncImagePixels(image)) |
2110 | 0 | { |
2111 | 0 | status=MagickFail; |
2112 | 0 | break; |
2113 | 0 | } |
2114 | 12.0M | #if !USE_LIBJPEG_PROGRESS |
2115 | 12.0M | if (QuantumTick(y,image->rows)) |
2116 | 3.44M | if (!MagickMonitorFormatted(y,image->rows,exception,LoadImageText, |
2117 | 3.44M | image->filename, |
2118 | 3.44M | image->columns,image->rows)) |
2119 | 0 | { |
2120 | 0 | status=MagickFail; |
2121 | 0 | jpeg_abort_decompress(&jpeg_info); |
2122 | 0 | break; |
2123 | 0 | } |
2124 | 12.0M | #endif /* !USE_LIBJPEG_PROGRESS */ |
2125 | 12.0M | } |
2126 | | /* |
2127 | | Free jpeg resources. |
2128 | | */ |
2129 | 161k | if (status == MagickPass) |
2130 | 157k | { |
2131 | | /* |
2132 | | jpeg_finish_decompress() may throw an exception while it is |
2133 | | finishing the remainder of the JPEG file. At this point we |
2134 | | have already decoded the image so we handle exceptions from |
2135 | | jpeg_finish_decompress() specially, mapping reported |
2136 | | exceptions as warnings rather than errors. We try using |
2137 | | jpeg_finish_decompress() and if it results in a longjmp(), |
2138 | | then we skip over it again. |
2139 | | */ |
2140 | 157k | client_data->completed=MagickTrue; |
2141 | 157k | if (setjmp(client_data->error_recovery) != 0) |
2142 | 21.6k | { |
2143 | 21.6k | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2144 | 21.6k | "Setjmp return from longjmp!"); |
2145 | 21.6k | } |
2146 | 136k | else |
2147 | 136k | { |
2148 | 136k | (void) jpeg_finish_decompress(&jpeg_info); |
2149 | 136k | } |
2150 | 157k | } |
2151 | 161k | jpeg_destroy_decompress(&jpeg_info); |
2152 | 161k | MagickFreeResourceLimitedMemory(void *,jpeg_pixels.t.v); |
2153 | 161k | client_data=FreeMagickClientData(client_data); |
2154 | 161k | CloseBlob(image); |
2155 | | |
2156 | | /* |
2157 | | Retrieve image orientation from EXIF (if present) and store in |
2158 | | image. |
2159 | | |
2160 | | EXIF orientation enumerations match TIFF enumerations, which happen |
2161 | | to match the enumeration values used by GraphicsMagick. |
2162 | | */ |
2163 | 161k | if (status == MagickPass) |
2164 | 157k | { |
2165 | 157k | const ImageAttribute |
2166 | 157k | *attribute; |
2167 | | |
2168 | 157k | attribute = GetImageAttribute(image,"EXIF:Orientation"); |
2169 | 157k | if ((attribute != (const ImageAttribute *) NULL) && |
2170 | 1.67k | (attribute->value != (char *) NULL)) |
2171 | 1.67k | { |
2172 | 1.67k | int |
2173 | 1.67k | orientation; |
2174 | | |
2175 | 1.67k | orientation=MagickAtoI(attribute->value); |
2176 | 1.67k | if ((orientation > UndefinedOrientation) && |
2177 | 213 | (orientation <= LeftBottomOrientation)) |
2178 | 71 | image->orientation=(OrientationType) orientation; |
2179 | 1.67k | } |
2180 | 157k | } |
2181 | 161k | if (image->logging) |
2182 | 161k | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"return"); |
2183 | 161k | GetImageException(image,exception); |
2184 | 161k | StopTimer(&image->timer); |
2185 | 161k | return(image); |
2186 | 161k | } |
2187 | | #endif |
2188 | | |
2189 | | /* |
2190 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2191 | | % % |
2192 | | % % |
2193 | | % % |
2194 | | % R e g i s t e r J P E G I m a g e % |
2195 | | % % |
2196 | | % % |
2197 | | % % |
2198 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2199 | | % |
2200 | | % Method RegisterJPEGImage adds attributes for the JPEG image format to |
2201 | | % the list of supported formats. The attributes include the image format |
2202 | | % tag, a method to read and/or write the format, whether the format |
2203 | | % supports the saving of more than one frame to the same file or blob, |
2204 | | % whether the format supports native in-memory I/O, and a brief |
2205 | | % description of the format. |
2206 | | % |
2207 | | % The format of the RegisterJPEGImage method is: |
2208 | | % |
2209 | | % RegisterJPEGImage(void) |
2210 | | % |
2211 | | */ |
2212 | | ModuleExport void RegisterJPEGImage(void) |
2213 | 15 | { |
2214 | 15 | static const char |
2215 | 15 | description[]="Joint Photographic Experts Group JFIF format"; |
2216 | | |
2217 | 15 | #if defined(HasJPEG) && defined(JPEG_LIB_VERSION) |
2218 | 15 | static const char |
2219 | 15 | version[] = "IJG JPEG " DefineValueToString(JPEG_LIB_VERSION); |
2220 | 15 | #define HAVE_JPEG_VERSION |
2221 | 15 | #endif |
2222 | | |
2223 | 15 | MagickInfo |
2224 | 15 | *entry; |
2225 | | |
2226 | 15 | MagickBool |
2227 | 15 | thread_support; |
2228 | | |
2229 | 15 | #if defined(SETJMP_IS_THREAD_SAFE) && (SETJMP_IS_THREAD_SAFE) |
2230 | 15 | thread_support=MagickTrue; /* libjpeg is thread safe */ |
2231 | | #else |
2232 | | thread_support=MagickFalse; /* libjpeg is not thread safe */ |
2233 | | #endif |
2234 | | |
2235 | 15 | entry=SetMagickInfo("JPEG"); |
2236 | 15 | entry->thread_support=thread_support; |
2237 | 15 | #if defined(HasJPEG) |
2238 | 15 | entry->decoder=(DecoderHandler) ReadJPEGImage; |
2239 | 15 | entry->encoder=(EncoderHandler) WriteJPEGImage; |
2240 | 15 | #endif |
2241 | 15 | entry->magick=(MagickHandler) IsJPEG; |
2242 | 15 | entry->adjoin=False; |
2243 | 15 | entry->description=description; |
2244 | 15 | #if defined(HAVE_JPEG_VERSION) |
2245 | 15 | entry->version=version; |
2246 | 15 | #endif |
2247 | 15 | entry->module="JPEG"; |
2248 | 15 | entry->coder_class=PrimaryCoderClass; |
2249 | 15 | (void) RegisterMagickInfo(entry); |
2250 | | |
2251 | 15 | entry=SetMagickInfo("JPG"); |
2252 | 15 | entry->thread_support=thread_support; |
2253 | 15 | #if defined(HasJPEG) |
2254 | 15 | entry->decoder=(DecoderHandler) ReadJPEGImage; |
2255 | 15 | entry->encoder=(EncoderHandler) WriteJPEGImage; |
2256 | 15 | #endif |
2257 | 15 | entry->adjoin=False; |
2258 | 15 | entry->description=description; |
2259 | 15 | #if defined(HAVE_JPEG_VERSION) |
2260 | 15 | entry->version=version; |
2261 | 15 | #endif |
2262 | 15 | entry->module="JPEG"; |
2263 | 15 | entry->coder_class=PrimaryCoderClass; |
2264 | 15 | (void) RegisterMagickInfo(entry); |
2265 | 15 | } |
2266 | | |
2267 | | /* |
2268 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2269 | | % % |
2270 | | % % |
2271 | | % % |
2272 | | % U n r e g i s t e r J P E G I m a g e % |
2273 | | % % |
2274 | | % % |
2275 | | % % |
2276 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2277 | | % |
2278 | | % Method UnregisterJPEGImage removes format registrations made by the |
2279 | | % JPEG module from the list of supported formats. |
2280 | | % |
2281 | | % The format of the UnregisterJPEGImage method is: |
2282 | | % |
2283 | | % UnregisterJPEGImage(void) |
2284 | | % |
2285 | | */ |
2286 | | ModuleExport void UnregisterJPEGImage(void) |
2287 | 0 | { |
2288 | 0 | (void) UnregisterMagickInfo("JPEG"); |
2289 | 0 | (void) UnregisterMagickInfo("JPG"); |
2290 | 0 | } |
2291 | | |
2292 | | #if defined(HasJPEG) |
2293 | | /* |
2294 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2295 | | % % |
2296 | | % % |
2297 | | % % |
2298 | | % W r i t e J P E G I m a g e % |
2299 | | % % |
2300 | | % % |
2301 | | % % |
2302 | | %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% |
2303 | | % |
2304 | | % Method WriteJPEGImage writes a JPEG image file and returns it. It |
2305 | | % allocates the memory necessary for the new Image structure and returns a |
2306 | | % pointer to the new image. |
2307 | | % |
2308 | | % The format of the WriteJPEGImage method is: |
2309 | | % |
2310 | | % unsigned int WriteJPEGImage(const ImageInfo *image_info,Image *image) |
2311 | | % |
2312 | | % A description of each parameter follows: |
2313 | | % |
2314 | | % o status: Method WriteJPEGImage return True if the image is written. |
2315 | | % False is returned is there is of a memory shortage or if the image |
2316 | | % file cannot be opened for writing. |
2317 | | % |
2318 | | % o image_info: Specifies a pointer to a ImageInfo structure. |
2319 | | % |
2320 | | % o jpeg_image: A pointer to an Image structure. |
2321 | | % |
2322 | | % |
2323 | | */ |
2324 | | |
2325 | | static boolean EmptyOutputBuffer(j_compress_ptr cinfo) |
2326 | 7.45k | { |
2327 | 7.45k | DestinationManager |
2328 | 7.45k | *destination; |
2329 | | |
2330 | 7.45k | destination=(DestinationManager *) cinfo->dest; |
2331 | 7.45k | destination->manager.free_in_buffer=WriteBlob(destination->image, |
2332 | 7.45k | MaxBufferExtent,(char *) destination->buffer); |
2333 | 7.45k | if (destination->manager.free_in_buffer != MaxBufferExtent) |
2334 | 6 | ERREXIT(cinfo,JERR_FILE_WRITE); |
2335 | 7.45k | destination->manager.next_output_byte=destination->buffer; |
2336 | 7.45k | return(TRUE); |
2337 | 7.45k | } |
2338 | | |
2339 | | static void InitializeDestination(j_compress_ptr cinfo) |
2340 | 726 | { |
2341 | 726 | DestinationManager |
2342 | 726 | *destination; |
2343 | | |
2344 | 726 | destination=(DestinationManager *) cinfo->dest; |
2345 | 726 | destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small) |
2346 | 726 | ((j_common_ptr) cinfo,JPOOL_IMAGE,MaxBufferExtent*sizeof(JOCTET)); |
2347 | 726 | destination->manager.next_output_byte=destination->buffer; |
2348 | 726 | destination->manager.free_in_buffer=MaxBufferExtent; |
2349 | 726 | } |
2350 | | |
2351 | | static void TerminateDestination(j_compress_ptr cinfo) |
2352 | 720 | { |
2353 | 720 | DestinationManager |
2354 | 720 | *destination; |
2355 | | |
2356 | 720 | destination=(DestinationManager *) cinfo->dest; |
2357 | 720 | if ((MaxBufferExtent-(int) destination->manager.free_in_buffer) > 0) |
2358 | 719 | { |
2359 | 719 | size_t |
2360 | 719 | number_bytes; |
2361 | | |
2362 | 719 | number_bytes=WriteBlob(destination->image,MaxBufferExtent- |
2363 | 719 | destination->manager.free_in_buffer,(char *) destination->buffer); |
2364 | 719 | if (number_bytes != (MaxBufferExtent-destination->manager.free_in_buffer)) |
2365 | 3 | ERREXIT(cinfo,JERR_FILE_WRITE); |
2366 | 719 | } |
2367 | 720 | } |
2368 | | |
2369 | | /* |
2370 | | Output generic APPN profile |
2371 | | */ |
2372 | | static void WriteAPPNProfile(j_compress_ptr jpeg_info, |
2373 | | const unsigned char *profile, |
2374 | | const size_t profile_length, |
2375 | | const char * profile_name) |
2376 | 642 | { |
2377 | 642 | size_t |
2378 | 642 | j; |
2379 | | |
2380 | 642 | int |
2381 | 642 | marker_id; |
2382 | | |
2383 | 642 | marker_id=JPEG_APP0+(int) MagickAtoL(profile_name+3); |
2384 | 1.29k | for (j=0; j < profile_length; j+=65533L) |
2385 | 652 | jpeg_write_marker(jpeg_info,marker_id, |
2386 | 652 | profile+j,(int) |
2387 | 652 | Min(profile_length-j,65533L)); |
2388 | 642 | } |
2389 | | |
2390 | | /* |
2391 | | Output EXIF profile |
2392 | | */ |
2393 | | static void WriteEXIFProfile(j_compress_ptr jpeg_info, |
2394 | | const unsigned char *profile, |
2395 | | const size_t profile_length) |
2396 | 65 | { |
2397 | 65 | size_t |
2398 | 65 | j; |
2399 | | |
2400 | 148 | for (j=0; j < profile_length; j+=65533L) |
2401 | 83 | jpeg_write_marker(jpeg_info,JPEG_APP0+1, |
2402 | 83 | profile+j,(int) |
2403 | 83 | Min(profile_length-j,65533L)); |
2404 | 65 | } |
2405 | | |
2406 | | /* |
2407 | | Output ICC color profile as a APP marker. |
2408 | | */ |
2409 | | static void WriteICCProfile(j_compress_ptr jpeg_info, |
2410 | | const unsigned char *color_profile, |
2411 | | const size_t profile_length) |
2412 | 18 | { |
2413 | 18 | register long |
2414 | 18 | i, |
2415 | 18 | j; |
2416 | | |
2417 | 61 | for (i=0; i < (long) profile_length; i+=65519) |
2418 | 43 | { |
2419 | 43 | unsigned char |
2420 | 43 | *profile; |
2421 | | |
2422 | 43 | size_t |
2423 | 43 | alloc_length, |
2424 | 43 | length=0; |
2425 | | |
2426 | | |
2427 | 43 | length=Min(profile_length-i,65519); |
2428 | 43 | alloc_length=length+14; |
2429 | 43 | profile=MagickAllocateResourceLimitedMemory(unsigned char *,alloc_length); |
2430 | 43 | if (profile == (unsigned char *) NULL) |
2431 | 0 | break; |
2432 | 43 | (void) strlcpy((char *) profile,"ICC_PROFILE",alloc_length); |
2433 | 43 | profile[12]=(unsigned char) ((i/65519)+1); |
2434 | 43 | profile[13]=(unsigned char) ((profile_length/65519)+1); |
2435 | 1.84M | for (j=0; j < (long) length; j++) |
2436 | 1.84M | profile[j+14]=color_profile[i+j]; |
2437 | 43 | jpeg_write_marker(jpeg_info,ICC_MARKER,profile,(unsigned int) alloc_length); |
2438 | 43 | MagickFreeResourceLimitedMemory(unsigned char *,profile); |
2439 | 43 | } |
2440 | 18 | } |
2441 | | |
2442 | | /* |
2443 | | Output binary Photoshop resource data using an APP marker. |
2444 | | */ |
2445 | | static void WriteIPTCProfile(j_compress_ptr jpeg_info, |
2446 | | const unsigned char *iptc_profile, |
2447 | | const size_t profile_length) |
2448 | 17 | { |
2449 | 17 | register long |
2450 | 17 | i; |
2451 | | |
2452 | 17 | unsigned long |
2453 | 17 | roundup, |
2454 | 17 | tag_length; |
2455 | | |
2456 | | #ifdef GET_ONLY_IPTC_DATA |
2457 | | tag_length=26; |
2458 | | #else |
2459 | 17 | tag_length=14; |
2460 | 17 | #endif |
2461 | 62 | for (i=0; i < (long) profile_length; i+=65500) |
2462 | 45 | { |
2463 | 45 | size_t |
2464 | 45 | length; |
2465 | | |
2466 | 45 | unsigned char |
2467 | 45 | *profile; |
2468 | | |
2469 | 45 | length=Min(profile_length-i,65500); |
2470 | 45 | roundup=(length & 0x01); /* round up for Photoshop */ |
2471 | 45 | profile=MagickAllocateResourceLimitedMemory(unsigned char *,length+roundup+tag_length); |
2472 | 45 | if (profile == (unsigned char *) NULL) |
2473 | 0 | break; |
2474 | | #ifdef GET_ONLY_IPTC_DATA |
2475 | | (void) memcpy(profile,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24); |
2476 | | profile[13]=0x00; |
2477 | | profile[24]=length >> 8; |
2478 | | profile[25]=length & 0xff; |
2479 | | #else |
2480 | 45 | (void) memcpy(profile,"Photoshop 3.0 ",14); |
2481 | 45 | profile[13]=0x00; |
2482 | 45 | #endif |
2483 | 45 | (void) memcpy(&(profile[tag_length]),&(iptc_profile[i]),length); |
2484 | 45 | if (roundup) |
2485 | 6 | profile[length+tag_length]=0; |
2486 | 45 | jpeg_write_marker(jpeg_info,IPTC_MARKER,profile,(unsigned int) |
2487 | 45 | (length+roundup+tag_length)); |
2488 | 45 | MagickFreeResourceLimitedMemory(unsigned char *,profile); |
2489 | 45 | } |
2490 | 17 | } |
2491 | | |
2492 | | /* |
2493 | | Output Adobe XMP XML profile. |
2494 | | */ |
2495 | | static void WriteXMPProfile(j_compress_ptr jpeg_info, |
2496 | | const unsigned char *profile, |
2497 | | const size_t profile_length) |
2498 | 35 | { |
2499 | 35 | size_t |
2500 | 35 | count, |
2501 | 35 | index, |
2502 | 35 | header_length, |
2503 | 35 | total_length; |
2504 | | |
2505 | 35 | unsigned int |
2506 | 35 | marker_length, |
2507 | 35 | remaining; |
2508 | | |
2509 | 35 | header_length=strlen(xmp_std_header)+1; /* Include terminating null */ |
2510 | 35 | total_length=header_length+profile_length; |
2511 | | /* XMP profile must be no larger than range of 'unsigned int' */ |
2512 | 35 | remaining=(unsigned int) Min(UINT_MAX,total_length); |
2513 | | |
2514 | 35 | marker_length=Min(remaining,JPEG_MARKER_MAX_SIZE); |
2515 | 35 | jpeg_write_m_header(jpeg_info,XML_MARKER,marker_length); |
2516 | 35 | count=marker_length; |
2517 | 35 | { |
2518 | 1.05k | for (index=0 ; index < header_length; index++) |
2519 | 1.01k | { |
2520 | 1.01k | jpeg_write_m_byte(jpeg_info,xmp_std_header[index]); |
2521 | 1.01k | --remaining; |
2522 | 1.01k | --count; |
2523 | 1.01k | } |
2524 | 35 | } |
2525 | | |
2526 | 2.33M | for (index=0; remaining > 0; --remaining) |
2527 | 2.33M | { |
2528 | 2.33M | if (count == 0) |
2529 | 31 | { |
2530 | 31 | marker_length=Min(remaining,JPEG_MARKER_MAX_SIZE); |
2531 | 31 | jpeg_write_m_header(jpeg_info,XML_MARKER,marker_length); |
2532 | 31 | count=marker_length; |
2533 | 31 | } |
2534 | 2.33M | jpeg_write_m_byte(jpeg_info,profile[index]); |
2535 | 2.33M | index++; |
2536 | 2.33M | count--; |
2537 | 2.33M | } |
2538 | 35 | } |
2539 | | |
2540 | | /* |
2541 | | Output profiles to JPEG stream. |
2542 | | */ |
2543 | | static void WriteProfiles(j_compress_ptr jpeg_info,Image *image) |
2544 | 726 | { |
2545 | 726 | const char |
2546 | 726 | *profile_name; |
2547 | | |
2548 | 726 | const unsigned char * |
2549 | 726 | profile; |
2550 | | |
2551 | 726 | ImageProfileIterator |
2552 | 726 | profile_iterator; |
2553 | | |
2554 | 726 | size_t |
2555 | 726 | profile_length=0; |
2556 | | |
2557 | 726 | profile_iterator=AllocateImageProfileIterator(image); |
2558 | 1.50k | while(NextImageProfile(profile_iterator,&profile_name,&profile, |
2559 | 1.50k | &profile_length) != MagickFail) |
2560 | 777 | { |
2561 | 777 | if (LocaleNCompare(profile_name,"APP",3) == 0) |
2562 | 642 | { |
2563 | 642 | WriteAPPNProfile(jpeg_info, profile, profile_length, profile_name); |
2564 | 642 | } |
2565 | 135 | else if (LocaleCompare(profile_name,"EXIF") == 0) |
2566 | 65 | { |
2567 | 65 | WriteEXIFProfile(jpeg_info, profile, profile_length); |
2568 | 65 | } |
2569 | 70 | else if ((LocaleCompare(profile_name,"ICM") == 0) || |
2570 | 52 | (LocaleCompare(profile_name,"ICC") == 0)) |
2571 | 18 | { |
2572 | 18 | WriteICCProfile(jpeg_info, profile, profile_length); |
2573 | 18 | } |
2574 | 52 | else if ((LocaleCompare(profile_name,"IPTC") == 0) || |
2575 | 35 | (LocaleCompare(profile_name,"8BIM") == 0)) |
2576 | 17 | { |
2577 | 17 | WriteIPTCProfile(jpeg_info, profile, profile_length); |
2578 | 17 | } |
2579 | 35 | else if (LocaleCompare(profile_name,"XMP") == 0) |
2580 | 35 | { |
2581 | 35 | WriteXMPProfile(jpeg_info, profile, profile_length); |
2582 | 35 | } |
2583 | 0 | else |
2584 | 0 | { |
2585 | | /* |
2586 | | Skip unknown profile type |
2587 | | */ |
2588 | 0 | if (image->logging) |
2589 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2590 | 0 | "Skipped Profile: %s, %" |
2591 | 0 | MAGICK_SIZE_T_F "u bytes", |
2592 | 0 | profile_name, |
2593 | 0 | (MAGICK_SIZE_T) profile_length); |
2594 | 0 | continue; |
2595 | 0 | } |
2596 | | |
2597 | 777 | if (image->logging) |
2598 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2599 | 0 | "Wrote Profile: %s, %" |
2600 | 0 | MAGICK_SIZE_T_F "u bytes",profile_name, |
2601 | 0 | (MAGICK_SIZE_T) profile_length); |
2602 | 777 | } |
2603 | 726 | DeallocateImageProfileIterator(profile_iterator); |
2604 | 726 | } |
2605 | | |
2606 | | static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image) |
2607 | 726 | { |
2608 | 726 | DestinationManager |
2609 | 726 | *destination; |
2610 | | |
2611 | 726 | cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) |
2612 | 726 | ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager)); |
2613 | 726 | destination=(DestinationManager *) cinfo->dest; |
2614 | 726 | destination->manager.init_destination=InitializeDestination; |
2615 | 726 | destination->manager.empty_output_buffer=EmptyOutputBuffer; |
2616 | 726 | destination->manager.term_destination=TerminateDestination; |
2617 | 726 | destination->image=image; |
2618 | 726 | } |
2619 | | |
2620 | | /* |
2621 | | Format a libjpeg warning or trace event while encoding. Warnings |
2622 | | are converted to GraphicsMagick warning exceptions while traces are |
2623 | | optionally logged. |
2624 | | |
2625 | | JPEG message codes range from 0 to JMSG_LASTMSGCODE |
2626 | | */ |
2627 | | static void JPEGEncodeMessageHandler(j_common_ptr jpeg_info,int msg_level) |
2628 | 0 | { |
2629 | 0 | char |
2630 | 0 | message[JMSG_LENGTH_MAX]; |
2631 | |
|
2632 | 0 | struct jpeg_error_mgr |
2633 | 0 | *err; |
2634 | |
|
2635 | 0 | MagickClientData |
2636 | 0 | *client_data; |
2637 | |
|
2638 | 0 | Image |
2639 | 0 | *image; |
2640 | |
|
2641 | 0 | message[0]='\0'; |
2642 | 0 | err=jpeg_info->err; |
2643 | 0 | client_data=(MagickClientData *) jpeg_info->client_data; |
2644 | 0 | image=client_data->image; |
2645 | | /* msg_level is -1 for warnings, 0 and up for trace messages. */ |
2646 | 0 | if (msg_level < 0) |
2647 | 0 | { |
2648 | 0 | unsigned int strikes = 0; |
2649 | | /* A warning */ |
2650 | 0 | (err->format_message)(jpeg_info,message); |
2651 | |
|
2652 | 0 | if ((err->msg_code >= 0) && |
2653 | 0 | ((size_t) err->msg_code < ArraySize(client_data->warning_counts))) |
2654 | 0 | { |
2655 | 0 | client_data->warning_counts[err->msg_code]++; |
2656 | 0 | strikes=client_data->warning_counts[err->msg_code]; |
2657 | 0 | } |
2658 | |
|
2659 | 0 | if (image->logging) |
2660 | 0 | { |
2661 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2662 | 0 | "[%s] JPEG Warning[%u]: \"%s\"" |
2663 | 0 | " (code=%d " |
2664 | 0 | "parms=0x%02x,0x%02x," |
2665 | 0 | "0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)", |
2666 | 0 | image->filename, |
2667 | 0 | strikes, |
2668 | 0 | message,err->msg_code, |
2669 | 0 | err->msg_parm.i[0], err->msg_parm.i[1], |
2670 | 0 | err->msg_parm.i[2], err->msg_parm.i[3], |
2671 | 0 | err->msg_parm.i[4], err->msg_parm.i[5], |
2672 | 0 | err->msg_parm.i[6], err->msg_parm.i[7]); |
2673 | 0 | } |
2674 | | /* |
2675 | | if (strikes > client_data->max_warning_count) |
2676 | | { |
2677 | | ThrowException2(&image->exception,CorruptImageError,(char *) message, |
2678 | | image->filename); |
2679 | | longjmp(client_data->error_recovery,1); |
2680 | | } |
2681 | | |
2682 | | if ((err->num_warnings == 0) || |
2683 | | (err->trace_level >= 3)) |
2684 | | ThrowException2(&image->exception,CorruptImageWarning,message, |
2685 | | image->filename); |
2686 | | */ |
2687 | | /* JWRN_JPEG_EOF - "Premature end of JPEG file" */ |
2688 | 0 | err->num_warnings++; |
2689 | 0 | return /* False */; |
2690 | 0 | } |
2691 | 0 | else |
2692 | 0 | { |
2693 | | /* A trace message */ |
2694 | 0 | if ((image->logging) && (msg_level >= err->trace_level)) |
2695 | 0 | { |
2696 | 0 | (err->format_message)(jpeg_info,message); |
2697 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2698 | 0 | "[%s] JPEG Trace: \"%s\"",image->filename, |
2699 | 0 | message); |
2700 | 0 | } |
2701 | 0 | } |
2702 | 0 | return /* True */; |
2703 | 0 | } |
2704 | | |
2705 | | |
2706 | | static void JPEGEncodeProgressMonitor(j_common_ptr cinfo) |
2707 | 257k | { |
2708 | | #if USE_LIBJPEG_PROGRESS |
2709 | | struct jpeg_progress_mgr *p = cinfo->progress; |
2710 | | MagickClientData *client_data = (MagickClientData *) cinfo->client_data; |
2711 | | Image *image = client_data->image; |
2712 | | |
2713 | | #if 0 |
2714 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2715 | | "Progress: pass_counter=%ld, pass_limit=%ld," |
2716 | | " completed_passes=%d, total_passes=%d, filename=%s", |
2717 | | p->pass_counter, p->pass_limit, |
2718 | | p->completed_passes, p->total_passes, image->filename); |
2719 | | #endif |
2720 | | |
2721 | | if (QuantumTick(p->pass_counter,p->pass_limit)) |
2722 | | if (!MagickMonitorFormatted(p->pass_counter,p->pass_limit,&image->exception, |
2723 | | "[%s] Saving image: %lux%lu (pass %d of %d)... ", |
2724 | | image->filename, |
2725 | | image->columns,image->rows, |
2726 | | p->completed_passes+1, p->total_passes)) |
2727 | | { |
2728 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2729 | | "Quitting due to progress monitor"); |
2730 | | longjmp(client_data->error_recovery,1); |
2731 | | } |
2732 | | #else |
2733 | 257k | (void) cinfo; |
2734 | 257k | #endif /* USE_LIBJPEG_PROGRESS */ |
2735 | 257k | } |
2736 | | |
2737 | | |
2738 | | #define ThrowJPEGWriterException(code_,reason_,image_) \ |
2739 | 0 | { \ |
2740 | 0 | client_data=FreeMagickClientData(client_data); \ |
2741 | 0 | ThrowWriterException(code_,reason_,image_); \ |
2742 | 0 | } |
2743 | | |
2744 | | static MagickPassFail WriteJPEGImage(const ImageInfo *image_info,Image *imagep) |
2745 | 726 | { |
2746 | 726 | Image |
2747 | 726 | * volatile image = imagep; /* volatile to avoid "clobber" */ |
2748 | | |
2749 | 726 | MagickClientData |
2750 | 726 | *client_data = (MagickClientData *) NULL; |
2751 | | |
2752 | 726 | const ImageAttribute |
2753 | 726 | *attribute; |
2754 | | |
2755 | 726 | magick_jpeg_pixels_t |
2756 | 726 | jpeg_pixels; /* Contents freed by FreeMagickClientData() */ |
2757 | | |
2758 | 726 | char |
2759 | 726 | *sampling_factors, |
2760 | 726 | *preserve_settings; |
2761 | | |
2762 | 726 | const char |
2763 | 726 | *value; |
2764 | | |
2765 | 726 | long |
2766 | 726 | y; |
2767 | | |
2768 | 726 | register const PixelPacket |
2769 | 726 | *p; |
2770 | | |
2771 | 726 | register long |
2772 | 726 | x; |
2773 | | |
2774 | 726 | register unsigned long |
2775 | 726 | i; |
2776 | | |
2777 | 726 | struct jpeg_error_mgr |
2778 | 726 | jpeg_error; |
2779 | | |
2780 | 726 | struct jpeg_progress_mgr |
2781 | 726 | jpeg_progress; |
2782 | | |
2783 | 726 | struct jpeg_compress_struct |
2784 | 726 | jpeg_info; |
2785 | | |
2786 | 726 | MagickPassFail |
2787 | 726 | status; |
2788 | | |
2789 | 726 | unsigned long |
2790 | 726 | input_colorspace; |
2791 | | |
2792 | 726 | unsigned long |
2793 | 726 | quality; |
2794 | | |
2795 | 726 | magick_int64_t |
2796 | 726 | huffman_memory; |
2797 | | |
2798 | 726 | ImageCharacteristics |
2799 | 726 | characteristics; |
2800 | | |
2801 | | /* |
2802 | | Open image file. |
2803 | | */ |
2804 | 726 | assert(image_info != (const ImageInfo *) NULL); |
2805 | 726 | assert(image_info->signature == MagickSignature); |
2806 | 726 | assert(imagep != (Image *) NULL); |
2807 | 726 | assert(imagep->signature == MagickSignature); |
2808 | 726 | client_data=AllocateMagickClientData(); |
2809 | 726 | if (client_data == (MagickClientData *) NULL) |
2810 | 726 | ThrowJPEGWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
2811 | 726 | status=OpenBlob(image_info,image,WriteBinaryBlobMode,&image->exception); |
2812 | 726 | if (status == False) |
2813 | 726 | ThrowJPEGWriterException(FileOpenError,UnableToOpenFile,image); |
2814 | | |
2815 | 726 | (void) memset(&jpeg_pixels,0,sizeof(jpeg_pixels)); |
2816 | 726 | (void) memset(&jpeg_progress,0,sizeof(jpeg_progress)); |
2817 | 726 | (void) memset(&jpeg_info,0,sizeof(jpeg_info)); |
2818 | 726 | (void) memset(&jpeg_error,0,sizeof(jpeg_error)); |
2819 | | |
2820 | | /* |
2821 | | Set initial longjmp based error handler. |
2822 | | */ |
2823 | 726 | jpeg_info.client_data=(void *) image; |
2824 | 726 | jpeg_info.err=jpeg_std_error(&jpeg_error); |
2825 | 726 | jpeg_info.err->emit_message=/*(void (*)(j_common_ptr,int))*/ JPEGEncodeMessageHandler; |
2826 | 726 | jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler; |
2827 | 726 | client_data->image=image; |
2828 | 726 | client_data->max_warning_count=MaxWarningCount; |
2829 | | /* |
2830 | | Allow the user to set how many warnings of any given type are |
2831 | | allowed before promotion of the warning to a hard error. |
2832 | | */ |
2833 | 726 | if ((value=AccessDefinition(image_info,"jpeg","max-warnings"))) |
2834 | 0 | client_data->max_warning_count=strtol(value,(char **) NULL, 10); |
2835 | 726 | client_data->jpeg_pixels = &jpeg_pixels; |
2836 | 726 | jpeg_info.client_data=(void *) client_data; |
2837 | 726 | if (setjmp(client_data->error_recovery) != 0) |
2838 | 9 | { |
2839 | 9 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2840 | 9 | "Setjmp return from longjmp!"); |
2841 | 9 | jpeg_destroy_compress(&jpeg_info); |
2842 | 9 | client_data=FreeMagickClientData(client_data); |
2843 | 9 | CloseBlob(image); |
2844 | 9 | return MagickFail ; |
2845 | 9 | } |
2846 | | |
2847 | 717 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2848 | 717 | " Write JPEG Image: image->orientation = %d", |
2849 | 717 | image->orientation); |
2850 | | |
2851 | | /* |
2852 | | Transform image to user-requested colorspace. |
2853 | | */ |
2854 | 717 | if (UndefinedColorspace != image_info->colorspace) |
2855 | 0 | { |
2856 | 0 | if (image->logging) |
2857 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2858 | 0 | " Transforming colorspace from %s to %s", |
2859 | 0 | ColorspaceTypeToString(image->colorspace), |
2860 | 0 | ColorspaceTypeToString(image_info->colorspace)); |
2861 | 0 | (void) TransformColorspace(image,image_info->colorspace); |
2862 | 0 | } |
2863 | | |
2864 | | /* |
2865 | | This writer expects an RGB-compatible colorspace. If the current |
2866 | | colorspace is not RGB-compatible, then convert it to RGB. |
2867 | | |
2868 | | Convert RGB-compatible colorspaces (e.g. CineonLog) to RGB by |
2869 | | default. User can still override it by explicitly specifying the |
2870 | | desired colorspace. |
2871 | | */ |
2872 | 717 | if (((UndefinedColorspace == image_info->colorspace) && |
2873 | 726 | (!IsRGBCompatibleColorspace(image->colorspace))) || |
2874 | 696 | ((IsRGBCompatibleColorspace(image->colorspace) && |
2875 | 696 | !IsRGBColorspace(image->colorspace)))) |
2876 | 30 | { |
2877 | 30 | if (image->logging) |
2878 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2879 | 0 | " Transforming colorspace from %s to RGB", |
2880 | 0 | ColorspaceTypeToString(image->colorspace)); |
2881 | 30 | (void) TransformColorspace(image,RGBColorspace); |
2882 | 30 | } |
2883 | | |
2884 | | /* |
2885 | | Analyze image to be written. |
2886 | | */ |
2887 | 717 | if (!GetImageCharacteristics(image,&characteristics, |
2888 | 717 | (OptimizeType == image_info->type), |
2889 | 717 | &image->exception)) |
2890 | 0 | { |
2891 | 0 | client_data=FreeMagickClientData(client_data); |
2892 | 0 | CloseBlob(image); |
2893 | 0 | return MagickFail; |
2894 | 0 | } |
2895 | | |
2896 | 717 | jpeg_create_compress(&jpeg_info); |
2897 | 717 | JPEGDestinationManager(&jpeg_info,image); |
2898 | 717 | jpeg_info.image_width=(unsigned int) image->columns; |
2899 | 717 | jpeg_info.image_height=(unsigned int) image->rows; |
2900 | 717 | jpeg_info.input_components=3; |
2901 | 717 | jpeg_info.in_color_space=JCS_RGB; |
2902 | | |
2903 | | /* |
2904 | | Register our progress monitor |
2905 | | */ |
2906 | 717 | jpeg_progress.progress_monitor=(void (*)(j_common_ptr)) JPEGEncodeProgressMonitor; |
2907 | 717 | jpeg_info.progress=&jpeg_progress; |
2908 | | |
2909 | | /* |
2910 | | Set JPEG colorspace as per user request. |
2911 | | */ |
2912 | 717 | { |
2913 | 717 | MagickBool |
2914 | 717 | colorspace_set=MagickFalse; |
2915 | | |
2916 | 717 | if (IsCMYKColorspace(image_info->colorspace)) |
2917 | 0 | { |
2918 | 0 | jpeg_info.input_components=4; |
2919 | 0 | jpeg_info.in_color_space=JCS_CMYK; |
2920 | 0 | colorspace_set=MagickTrue; |
2921 | 0 | } |
2922 | 717 | else if (IsYCbCrColorspace(image_info->colorspace)) |
2923 | 0 | { |
2924 | 0 | jpeg_info.input_components=3; |
2925 | 0 | jpeg_info.in_color_space=JCS_YCbCr; |
2926 | 0 | colorspace_set=MagickTrue; |
2927 | 0 | } |
2928 | 717 | else if (IsGrayColorspace(image_info->colorspace)) |
2929 | 0 | { |
2930 | 0 | jpeg_info.input_components=1; |
2931 | 0 | jpeg_info.in_color_space=JCS_GRAYSCALE; |
2932 | 0 | colorspace_set=MagickTrue; |
2933 | 0 | } |
2934 | | |
2935 | 717 | if (!colorspace_set) |
2936 | 726 | { |
2937 | 726 | if (IsCMYKColorspace(image->colorspace)) |
2938 | 0 | { |
2939 | 0 | jpeg_info.input_components=4; |
2940 | 0 | jpeg_info.in_color_space=JCS_CMYK; |
2941 | 0 | } |
2942 | 726 | else if (IsYCbCrColorspace(image->colorspace)) |
2943 | 0 | { |
2944 | 0 | jpeg_info.input_components=3; |
2945 | 0 | jpeg_info.in_color_space=JCS_YCbCr; |
2946 | 0 | } |
2947 | 726 | else if ((IsGrayColorspace(image->colorspace) || |
2948 | 726 | (characteristics.grayscale))) |
2949 | 608 | { |
2950 | 608 | jpeg_info.input_components=1; |
2951 | 608 | jpeg_info.in_color_space=JCS_GRAYSCALE; |
2952 | 608 | } |
2953 | 118 | else |
2954 | 118 | { |
2955 | 118 | jpeg_info.input_components=3; |
2956 | 118 | jpeg_info.in_color_space=JCS_RGB; |
2957 | 118 | } |
2958 | 726 | } |
2959 | 717 | } |
2960 | | |
2961 | 717 | input_colorspace=UndefinedColorspace; |
2962 | 717 | quality=image_info->quality; |
2963 | | /* Check for -define jpeg:preserve-settings */ |
2964 | | /* ImageMagick: |
2965 | | GetImageOption(); |
2966 | | */ |
2967 | | /* GraphicsMagick */ |
2968 | 717 | preserve_settings=(char *) AccessDefinition(image_info,"jpeg", |
2969 | 717 | "preserve-settings"); |
2970 | | |
2971 | 717 | sampling_factors=image_info->sampling_factor; |
2972 | | |
2973 | 717 | if (preserve_settings) |
2974 | 0 | { |
2975 | 0 | if (image->logging) |
2976 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2977 | 0 | " JPEG:preserve-settings flag is defined."); |
2978 | | |
2979 | | /* Retrieve input file quality */ |
2980 | 0 | attribute=GetImageAttribute(image,"JPEG-Quality"); |
2981 | 0 | if ((attribute != (const ImageAttribute *) NULL) && |
2982 | 0 | (attribute->value != (char *) NULL)) |
2983 | 0 | { |
2984 | 0 | (void) sscanf(attribute->value,"%lu",&quality); |
2985 | 0 | if (image->logging) |
2986 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2987 | 0 | " Input quality=%lu",quality); |
2988 | 0 | } |
2989 | | |
2990 | | /* Retrieve input file colorspace */ |
2991 | 0 | attribute=GetImageAttribute(image,"JPEG-Colorspace"); |
2992 | 0 | if ((attribute != (const ImageAttribute *) NULL) && |
2993 | 0 | (attribute->value != (char *) NULL)) |
2994 | 0 | { |
2995 | 0 | (void) sscanf(attribute->value,"%lu",&input_colorspace); |
2996 | 0 | if (image->logging) |
2997 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
2998 | 0 | " Input colorspace=%lu",input_colorspace); |
2999 | 0 | } |
3000 | |
|
3001 | 0 | if (input_colorspace == (unsigned long) jpeg_info.in_color_space) |
3002 | 0 | { |
3003 | | /* Retrieve input sampling factors */ |
3004 | 0 | attribute=GetImageAttribute(image,"JPEG-Sampling-factors"); |
3005 | 0 | if ((attribute != (const ImageAttribute *) NULL) && |
3006 | 0 | (attribute->value != (char *) NULL)) |
3007 | 0 | { |
3008 | 0 | sampling_factors=attribute->value; |
3009 | 0 | if (image->logging) |
3010 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3011 | 0 | " Input sampling-factors=%s",sampling_factors); |
3012 | 0 | } |
3013 | 0 | } |
3014 | 0 | else |
3015 | 0 | { |
3016 | 0 | if (image->logging) |
3017 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3018 | 0 | " Input colorspace (%lu) != Output colorspace (%d)", |
3019 | 0 | input_colorspace,jpeg_info.in_color_space); |
3020 | 0 | } |
3021 | 0 | } |
3022 | | |
3023 | 717 | jpeg_set_defaults(&jpeg_info); |
3024 | | |
3025 | | /* |
3026 | | Determine bit depth (valid range in 8-16). |
3027 | | */ |
3028 | 717 | { |
3029 | 717 | int |
3030 | 717 | sample_size; |
3031 | | |
3032 | 717 | sample_size=sizeof(JSAMPLE)*8; |
3033 | 717 | if (sample_size > 8) |
3034 | 0 | sample_size=12; |
3035 | 717 | if ((jpeg_info.data_precision != 12) && |
3036 | 726 | (image->depth <= 8)) |
3037 | 705 | sample_size=8; |
3038 | 717 | jpeg_info.data_precision=sample_size; |
3039 | 717 | } |
3040 | | |
3041 | | /* |
3042 | | Allow the user to set/override the data precision (8/12/16) |
3043 | | */ |
3044 | 717 | if ((value=AccessDefinition(image_info,"jpeg","data-precision"))) |
3045 | 0 | { |
3046 | 0 | unsigned int data_precision_prop = 0; |
3047 | 0 | if (sscanf(value,"%u",&data_precision_prop) == 1) |
3048 | 0 | { |
3049 | 0 | switch(data_precision_prop) |
3050 | 0 | { |
3051 | 0 | default: |
3052 | 0 | case 8: |
3053 | 0 | jpeg_info.data_precision=8; |
3054 | 0 | break; |
3055 | 0 | #if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES |
3056 | 0 | case 12: |
3057 | 0 | jpeg_info.data_precision=12; |
3058 | 0 | break; |
3059 | 0 | #endif /* if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES */ |
3060 | 0 | #if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES |
3061 | 0 | #if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS |
3062 | 0 | #if defined(C_LOSSLESS_SUPPORTED) |
3063 | 0 | case 16: |
3064 | 0 | jpeg_info.data_precision=16; |
3065 | 0 | break; |
3066 | 0 | #endif /* if defined(C_LOSSLESS_SUPPORTED) */ |
3067 | 0 | #endif /* if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS */ |
3068 | 0 | #endif /* if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES */ |
3069 | 0 | } |
3070 | 0 | if (image->logging) |
3071 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3072 | 0 | " Set data precision: %u", jpeg_info.data_precision); |
3073 | 0 | } |
3074 | 0 | } |
3075 | 717 | if ((image->x_resolution == 0) || (image->y_resolution == 0)) |
3076 | 723 | { |
3077 | 723 | image->x_resolution=72.0; |
3078 | 723 | image->y_resolution=72.0; |
3079 | 723 | image->units=PixelsPerInchResolution; |
3080 | 723 | } |
3081 | 717 | if (image_info->density != (char *) NULL) |
3082 | 0 | { |
3083 | 0 | int |
3084 | 0 | count; |
3085 | | |
3086 | | /* FIXME: density should not be set via image_info->density |
3087 | | but removing this support may break some applications. */ |
3088 | 0 | count=GetMagickDimension(image_info->density,&image->x_resolution, |
3089 | 0 | &image->y_resolution,NULL,NULL); |
3090 | 0 | if (count == 1 ) |
3091 | 0 | image->y_resolution=image->x_resolution; |
3092 | 0 | } |
3093 | 717 | jpeg_info.density_unit=1; /* default to DPI */ |
3094 | 717 | if (image->logging) |
3095 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3096 | 0 | "Image resolution: %ld,%ld",(long) image->x_resolution, |
3097 | 0 | (long) image->y_resolution); |
3098 | 726 | if ((image->x_resolution >= 0) && (image->x_resolution < (double) SHRT_MAX) && |
3099 | 725 | (image->y_resolution >= 0) && (image->y_resolution < (double) SHRT_MAX)) |
3100 | 724 | { |
3101 | | /* |
3102 | | Set image resolution. |
3103 | | */ |
3104 | 724 | jpeg_info.write_JFIF_header=True; |
3105 | 724 | jpeg_info.X_density=(short) image->x_resolution; |
3106 | 724 | jpeg_info.Y_density=(short) image->y_resolution; |
3107 | 724 | if (image->units == PixelsPerInchResolution) |
3108 | 723 | jpeg_info.density_unit=1; |
3109 | 724 | if (image->units == PixelsPerCentimeterResolution) |
3110 | 1 | jpeg_info.density_unit=2; |
3111 | 724 | } |
3112 | | |
3113 | 717 | { |
3114 | 717 | const char |
3115 | 717 | *value; |
3116 | | |
3117 | | /* |
3118 | | Allow the user to select the DCT encoding algorithm. |
3119 | | */ |
3120 | 717 | if ((value=AccessDefinition(image_info,"jpeg","dct-method"))) |
3121 | 0 | { |
3122 | 0 | if (LocaleCompare(value,"ISLOW") == 0) |
3123 | 0 | { |
3124 | 0 | jpeg_info.dct_method=JDCT_ISLOW; |
3125 | 0 | if (image->logging) |
3126 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3127 | 0 | " Set DCT method ISLOW"); |
3128 | 0 | } |
3129 | 0 | else if (LocaleCompare(value,"IFAST") == 0) |
3130 | 0 | { |
3131 | 0 | jpeg_info.dct_method=JDCT_IFAST; |
3132 | 0 | if (image->logging) |
3133 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3134 | 0 | " Set DCT method IFAST"); |
3135 | 0 | } |
3136 | 0 | else if (LocaleCompare(value,"FLOAT") == 0) |
3137 | 0 | { |
3138 | 0 | jpeg_info.dct_method=JDCT_FLOAT; |
3139 | 0 | if (image->logging) |
3140 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3141 | 0 | " Set DCT method FLOAT"); |
3142 | 0 | } |
3143 | 0 | else if (LocaleCompare(value,"DEFAULT") == 0) |
3144 | 0 | { |
3145 | 0 | jpeg_info.dct_method=JDCT_DEFAULT; |
3146 | 0 | if (image->logging) |
3147 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3148 | 0 | " Set DCT method DEFAULT"); |
3149 | 0 | } |
3150 | 0 | else if (LocaleCompare(value,"FASTEST") == 0) |
3151 | 0 | { |
3152 | 0 | jpeg_info.dct_method=JDCT_FASTEST; |
3153 | 0 | if (image->logging) |
3154 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3155 | 0 | " Set DCT method FASTEST"); |
3156 | 0 | } |
3157 | 0 | } |
3158 | 717 | } |
3159 | | |
3160 | 717 | { |
3161 | 717 | const char |
3162 | 717 | *value; |
3163 | | |
3164 | 717 | huffman_memory = 0; |
3165 | | |
3166 | 717 | #if defined(C_ARITH_CODING_SUPPORTED) |
3167 | | /* |
3168 | | Allow the user to turn on/off arithmetic coder. |
3169 | | */ |
3170 | 717 | if ((value=AccessDefinition(image_info,"jpeg","arithmetic-coding"))) |
3171 | 0 | { |
3172 | 0 | if (LocaleCompare(value,"FALSE") == 0) |
3173 | 0 | { |
3174 | 0 | jpeg_info.arith_code = False; |
3175 | 0 | if (image->logging) |
3176 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3177 | 0 | " Disabled arithmetic coding"); |
3178 | 0 | } |
3179 | 0 | else |
3180 | 0 | { |
3181 | 0 | jpeg_info.arith_code = True; |
3182 | 0 | if (image->logging) |
3183 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3184 | 0 | " Enabled arithmetic coding"); |
3185 | 0 | } |
3186 | 0 | } |
3187 | 717 | if (!jpeg_info.arith_code) /* jpeg_info.optimize_coding must not be set to enable arithmetic. */ |
3188 | 726 | #endif /* if defined(C_ARITH_CODING_SUPPORTED) */ |
3189 | 726 | { |
3190 | 726 | if ((value=AccessDefinition(image_info,"jpeg","optimize-coding"))) |
3191 | 0 | { |
3192 | 0 | if (LocaleCompare(value,"FALSE") == 0) |
3193 | 0 | { |
3194 | 0 | jpeg_info.optimize_coding=MagickFalse; |
3195 | 0 | if (image->logging) |
3196 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3197 | 0 | " Disabled optimize coding"); |
3198 | 0 | } |
3199 | 0 | else |
3200 | 0 | { |
3201 | 0 | jpeg_info.optimize_coding=MagickTrue; |
3202 | 0 | if (image->logging) |
3203 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3204 | 0 | " Enabled optimize coding"); |
3205 | 0 | } |
3206 | 0 | } |
3207 | 726 | else |
3208 | 726 | { |
3209 | | /* |
3210 | | Huffman optimization requires that the whole image be buffered in |
3211 | | memory. Since this is such a large consumer, obtain a memory |
3212 | | resource for the memory to be consumed. If the memory resource |
3213 | | fails to be acquired, then don't enable huffman optimization. |
3214 | | */ |
3215 | 726 | huffman_memory=(magick_int64_t) jpeg_info.input_components* |
3216 | 726 | image->columns*image->rows*sizeof(JSAMPLE); |
3217 | 726 | jpeg_info.optimize_coding=AcquireMagickResource(MemoryResource, |
3218 | 726 | huffman_memory); |
3219 | 726 | } |
3220 | 726 | if (!jpeg_info.optimize_coding) |
3221 | 0 | huffman_memory=0; |
3222 | 726 | if (image->logging) |
3223 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3224 | 0 | " Huffman optimization is %s", |
3225 | 0 | (jpeg_info.optimize_coding ? "enabled" : "disabled")); |
3226 | 726 | } |
3227 | 717 | } |
3228 | | |
3229 | 717 | #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED) |
3230 | 717 | if (image_info->interlace == LineInterlace) |
3231 | 0 | jpeg_simple_progression(&jpeg_info); |
3232 | 717 | if (image->logging) |
3233 | 0 | { |
3234 | 0 | if (image_info->interlace == LineInterlace) |
3235 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3236 | 0 | "Interlace: progressive"); |
3237 | 0 | else |
3238 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3239 | 0 | "Interlace: nonprogressive"); |
3240 | 0 | } |
3241 | | #else |
3242 | | if (image->logging) |
3243 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3244 | | "Interlace: nonprogressive"); |
3245 | | #endif |
3246 | 717 | if (image->logging) |
3247 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3248 | 0 | "Compression: %s", |
3249 | 0 | CompressionTypeToString(image->compression)); |
3250 | 717 | if (image->compression == LosslessJPEGCompression) |
3251 | 0 | { |
3252 | 0 | #if defined(C_LOSSLESS_SUPPORTED) |
3253 | 0 | { |
3254 | 0 | int |
3255 | 0 | point_transform, |
3256 | 0 | predictor; |
3257 | |
|
3258 | 0 | predictor=1; /* range 1-7 */ |
3259 | 0 | point_transform=0; /* range 0 to precision-1 */ |
3260 | | |
3261 | | /* |
3262 | | Right-shift the input samples by the specified number of |
3263 | | bits as a form of color quantization. Useful range of 0 |
3264 | | to precision-1. Use zero for true lossless compression! |
3265 | | */ |
3266 | 0 | if ((value=AccessDefinition(image_info,"jpeg","lossless-precision"))) |
3267 | 0 | { |
3268 | 0 | int point_transform_v = 0; |
3269 | 0 | if ((sscanf(value,"%u",&point_transform_v) == 1) && (point_transform_v >= 0)) |
3270 | 0 | point_transform = point_transform_v; |
3271 | 0 | } |
3272 | |
|
3273 | 0 | if ((value=AccessDefinition(image_info,"jpeg","lossless-predictor"))) |
3274 | 0 | { |
3275 | 0 | int predictor_v = predictor; |
3276 | 0 | if ((sscanf(value,"%u",&predictor_v) == 1) && (predictor_v >= 0)) |
3277 | 0 | predictor = predictor_v; |
3278 | 0 | } |
3279 | |
|
3280 | 0 | if (image->logging) |
3281 | 0 | { |
3282 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3283 | 0 | "Compression: lossless"); |
3284 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3285 | 0 | "DPCM Predictor: %d",predictor); |
3286 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3287 | 0 | "DPCM Point Transform: %d",point_transform); |
3288 | 0 | } |
3289 | | #if !defined(LIBJPEG_TURBO_VERSION_NUMBER) |
3290 | | jpeg_simple_lossless(&jpeg_info, predictor, point_transform); |
3291 | | #elif defined(LIBJPEG_TURBO_VERSION_NUMBER) |
3292 | | jpeg_enable_lossless(&jpeg_info, predictor, point_transform); |
3293 | 0 | #endif |
3294 | 0 | } |
3295 | | #else |
3296 | | { |
3297 | | jpeg_set_quality(&jpeg_info,100,True); |
3298 | | if (image->logging) |
3299 | | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100"); |
3300 | | } |
3301 | | #endif |
3302 | 0 | } |
3303 | 717 | else |
3304 | 717 | { |
3305 | 717 | jpeg_set_quality(&jpeg_info,(int) quality,True); |
3306 | 717 | if (image->logging) |
3307 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %lu", |
3308 | 0 | quality); |
3309 | 717 | } |
3310 | | |
3311 | 717 | if (sampling_factors != (char *) NULL) |
3312 | 0 | { |
3313 | 0 | double |
3314 | 0 | hs[4]={1.0, 1.0, 1.0, 1.0}, |
3315 | 0 | vs[4]={1.0, 1.0, 1.0, 1.0}; |
3316 | |
|
3317 | 0 | long |
3318 | 0 | count; |
3319 | | |
3320 | | /* |
3321 | | Set sampling factors. |
3322 | | */ |
3323 | 0 | count=sscanf(sampling_factors,"%lfx%lf,%lfx%lf,%lfx%lf,%lfx%lf", |
3324 | 0 | &hs[0],&vs[0],&hs[1],&vs[1],&hs[2],&vs[2],&hs[3],&vs[3]); |
3325 | |
|
3326 | 0 | if (count%2 == 1) |
3327 | 0 | vs[count/2]=hs[count/2]; |
3328 | |
|
3329 | 0 | for (i=0; i < 4; i++) |
3330 | 0 | { |
3331 | 0 | jpeg_info.comp_info[i].h_samp_factor=(int) hs[i]; |
3332 | 0 | jpeg_info.comp_info[i].v_samp_factor=(int) vs[i]; |
3333 | 0 | } |
3334 | 0 | for (; i < MAX_COMPONENTS; i++) |
3335 | 0 | { |
3336 | 0 | jpeg_info.comp_info[i].h_samp_factor=1; |
3337 | 0 | jpeg_info.comp_info[i].v_samp_factor=1; |
3338 | 0 | } |
3339 | 0 | } |
3340 | 717 | else |
3341 | 717 | { |
3342 | 717 | if (quality >= 90) |
3343 | 0 | for (i=0; i < MAX_COMPONENTS; i++) |
3344 | 0 | { |
3345 | 0 | jpeg_info.comp_info[i].h_samp_factor=1; |
3346 | 0 | jpeg_info.comp_info[i].v_samp_factor=1; |
3347 | 0 | } |
3348 | 717 | } |
3349 | | |
3350 | 717 | if (image->logging) |
3351 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3352 | 0 | "Starting JPEG compression"); |
3353 | 717 | jpeg_start_compress(&jpeg_info,True); |
3354 | 717 | if (image->logging) |
3355 | 0 | { |
3356 | 0 | if (image->storage_class == PseudoClass) |
3357 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3358 | 0 | "Storage class: PseudoClass"); |
3359 | 0 | else |
3360 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3361 | 0 | "Storage class: DirectClass"); |
3362 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %u", |
3363 | 0 | image->depth); |
3364 | 0 | if (image->colors) |
3365 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3366 | 0 | "Number of colors: %u",image->colors); |
3367 | 0 | else |
3368 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3369 | 0 | "Number of colors: unspecified"); |
3370 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3371 | 0 | "JPEG data precision: %d",(int) jpeg_info.data_precision); |
3372 | 0 | if (IsCMYKColorspace(image_info->colorspace)) |
3373 | 0 | { |
3374 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3375 | 0 | "Storage class: DirectClass"); |
3376 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3377 | 0 | "Colorspace: CMYK"); |
3378 | 0 | } |
3379 | 0 | else if (IsYCbCrColorspace(image_info->colorspace)) |
3380 | 0 | { |
3381 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3382 | 0 | "Colorspace: YCbCr"); |
3383 | 0 | } |
3384 | 0 | if (IsCMYKColorspace(image->colorspace)) |
3385 | 0 | { |
3386 | | /* A CMYK space */ |
3387 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3388 | 0 | "Colorspace: CMYK"); |
3389 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3390 | 0 | "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d", |
3391 | 0 | jpeg_info.comp_info[0].h_samp_factor, |
3392 | 0 | jpeg_info.comp_info[0].v_samp_factor, |
3393 | 0 | jpeg_info.comp_info[1].h_samp_factor, |
3394 | 0 | jpeg_info.comp_info[1].v_samp_factor, |
3395 | 0 | jpeg_info.comp_info[2].h_samp_factor, |
3396 | 0 | jpeg_info.comp_info[2].v_samp_factor, |
3397 | 0 | jpeg_info.comp_info[3].h_samp_factor, |
3398 | 0 | jpeg_info.comp_info[3].v_samp_factor); |
3399 | 0 | } |
3400 | 0 | else if (IsGrayColorspace(image->colorspace)) |
3401 | 0 | { |
3402 | | /* A gray space */ |
3403 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3404 | 0 | "Colorspace: GRAY"); |
3405 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3406 | 0 | "Sampling factors: %dx%d", |
3407 | 0 | jpeg_info.comp_info[0].h_samp_factor, |
3408 | 0 | jpeg_info.comp_info[0].v_samp_factor); |
3409 | 0 | } |
3410 | 0 | else if (IsRGBCompatibleColorspace(image->colorspace)) |
3411 | 0 | { |
3412 | | /* An RGB space */ |
3413 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3414 | 0 | " Image colorspace is RGB"); |
3415 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3416 | 0 | "Sampling factors: %dx%d,%dx%d,%dx%d", |
3417 | 0 | jpeg_info.comp_info[0].h_samp_factor, |
3418 | 0 | jpeg_info.comp_info[0].v_samp_factor, |
3419 | 0 | jpeg_info.comp_info[1].h_samp_factor, |
3420 | 0 | jpeg_info.comp_info[1].v_samp_factor, |
3421 | 0 | jpeg_info.comp_info[2].h_samp_factor, |
3422 | 0 | jpeg_info.comp_info[2].v_samp_factor); |
3423 | 0 | } |
3424 | 0 | else if (IsYCbCrColorspace(image->colorspace)) |
3425 | 0 | { |
3426 | | /* A YCbCr space */ |
3427 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3428 | 0 | "Colorspace: YCbCr"); |
3429 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3430 | 0 | "Sampling factors: %dx%d,%dx%d,%dx%d", |
3431 | 0 | jpeg_info.comp_info[0].h_samp_factor, |
3432 | 0 | jpeg_info.comp_info[0].v_samp_factor, |
3433 | 0 | jpeg_info.comp_info[1].h_samp_factor, |
3434 | 0 | jpeg_info.comp_info[1].v_samp_factor, |
3435 | 0 | jpeg_info.comp_info[2].h_samp_factor, |
3436 | 0 | jpeg_info.comp_info[2].v_samp_factor); |
3437 | 0 | } |
3438 | 0 | else |
3439 | 0 | { |
3440 | | /* Some other color space */ |
3441 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d", |
3442 | 0 | image->colorspace); |
3443 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3444 | 0 | "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d", |
3445 | 0 | jpeg_info.comp_info[0].h_samp_factor, |
3446 | 0 | jpeg_info.comp_info[0].v_samp_factor, |
3447 | 0 | jpeg_info.comp_info[1].h_samp_factor, |
3448 | 0 | jpeg_info.comp_info[1].v_samp_factor, |
3449 | 0 | jpeg_info.comp_info[2].h_samp_factor, |
3450 | 0 | jpeg_info.comp_info[2].v_samp_factor, |
3451 | 0 | jpeg_info.comp_info[3].h_samp_factor, |
3452 | 0 | jpeg_info.comp_info[3].v_samp_factor); |
3453 | 0 | } |
3454 | 0 | } |
3455 | | /* |
3456 | | Write JPEG profiles. |
3457 | | */ |
3458 | 717 | attribute=GetImageAttribute(image,"comment"); |
3459 | 717 | if ((attribute != (const ImageAttribute *) NULL) && |
3460 | 21 | (attribute->value != (char *) NULL)) |
3461 | 57 | for (i=0; i < strlen(attribute->value); i+=65533L) |
3462 | 36 | jpeg_write_marker(&jpeg_info,JPEG_COM,(unsigned char *) attribute->value+ |
3463 | 36 | i,(int) Min(strlen(attribute->value+i),65533L)); |
3464 | | #if 0 |
3465 | | WriteICCProfile(&jpeg_info,image); |
3466 | | WriteIPTCProfile(&jpeg_info,image); |
3467 | | WriteXMPProfile(&jpeg_info,image); |
3468 | | #endif |
3469 | 717 | WriteProfiles(&jpeg_info,image); |
3470 | | /* |
3471 | | Convert MIFF to JPEG raster pixels. |
3472 | | */ |
3473 | 717 | switch (jpeg_info.data_precision) |
3474 | 717 | { |
3475 | 0 | #if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES |
3476 | 0 | #if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS |
3477 | 0 | #if defined(C_LOSSLESS_SUPPORTED) |
3478 | 0 | case 16: |
3479 | 0 | jpeg_pixels.t.j16 = |
3480 | 0 | MagickAllocateResourceLimitedClearedArray(J16SAMPLE *, |
3481 | 0 | jpeg_info.input_components, |
3482 | 0 | MagickArraySize(image->columns, |
3483 | 0 | sizeof(J16SAMPLE))); |
3484 | 0 | break; |
3485 | 0 | #endif /* if defined(C_LOSSLESS_SUPPORTED) */ |
3486 | 0 | #endif /* if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS */ |
3487 | 0 | #endif /* if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES */ |
3488 | 0 | #if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES |
3489 | 0 | case 12: |
3490 | 0 | jpeg_pixels.t.j12 = |
3491 | 0 | MagickAllocateResourceLimitedClearedArray(J12SAMPLE *, |
3492 | 0 | jpeg_info.input_components, |
3493 | 0 | MagickArraySize(image->columns, |
3494 | 0 | sizeof(J12SAMPLE))); |
3495 | 0 | break; |
3496 | 0 | #endif /* if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES */ |
3497 | 726 | default: |
3498 | 726 | { |
3499 | 726 | jpeg_pixels.t.j= |
3500 | 726 | MagickAllocateResourceLimitedArray(JSAMPLE *, |
3501 | 726 | jpeg_info.input_components, |
3502 | 726 | MagickArraySize(image->columns, |
3503 | 726 | sizeof(JSAMPLE))); |
3504 | 726 | } |
3505 | 717 | } |
3506 | 726 | if (jpeg_pixels.t.v == (JSAMPLE *) NULL) |
3507 | 0 | { |
3508 | 0 | if (huffman_memory) |
3509 | 0 | LiberateMagickResource(MemoryResource,huffman_memory); |
3510 | 0 | ThrowJPEGWriterException(ResourceLimitError,MemoryAllocationFailed,image); |
3511 | 0 | } |
3512 | 726 | client_data->jpeg_pixels = &jpeg_pixels; |
3513 | | |
3514 | 726 | if (image->logging) |
3515 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3516 | 0 | "Writing %d components, %u bits per component," |
3517 | 0 | " with colorspace %s...", |
3518 | 0 | jpeg_info.input_components, |
3519 | 0 | jpeg_info.data_precision, |
3520 | 0 | JPEGColorSpaceToString(jpeg_info.in_color_space)); |
3521 | | |
3522 | 232k | for (y=0; y < (long) image->rows; y++) |
3523 | 232k | { |
3524 | 232k | p=AcquireImagePixels(image,0,y,image->columns,1,&image->exception); |
3525 | 232k | if (p == (const PixelPacket *) NULL) |
3526 | 0 | break; |
3527 | | |
3528 | 232k | #if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES |
3529 | 232k | #if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS |
3530 | 232k | #if defined(C_LOSSLESS_SUPPORTED) |
3531 | 232k | if (jpeg_info.data_precision == 16) |
3532 | 0 | { |
3533 | 0 | { |
3534 | 0 | J16SAMPROW |
3535 | 0 | scanline[1]; |
3536 | |
|
3537 | 0 | if (jpeg_info.in_color_space == JCS_GRAYSCALE) |
3538 | 0 | { /* Start deep JCS_GRAYSCALE */ |
3539 | 0 | if (image->is_grayscale) |
3540 | 0 | { |
3541 | 0 | i=0; |
3542 | 0 | for (x=0; x < (long) image->columns; x++) |
3543 | 0 | { |
3544 | 0 | jpeg_pixels.t.j16[i++]=(J16SAMPLE)(ScaleQuantumToShort(GetGraySample(p))); |
3545 | 0 | p++; |
3546 | 0 | } |
3547 | 0 | } |
3548 | 0 | else |
3549 | 0 | { |
3550 | 0 | i=0; |
3551 | 0 | for (x=0; x < (long) image->columns; x++) |
3552 | 0 | { |
3553 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(ScaleQuantumToShort(PixelIntensityToQuantum(p))); |
3554 | 0 | p++; |
3555 | 0 | } |
3556 | 0 | } |
3557 | 0 | } /* End deep JCS_GRAYSCALE */ |
3558 | 0 | else if ((jpeg_info.in_color_space == JCS_RGB) || |
3559 | 0 | (jpeg_info.in_color_space == JCS_YCbCr)) |
3560 | 0 | { /* Start deep JCS_RGB || JCS_YCbCr */ |
3561 | 0 | i=0; |
3562 | 0 | for (x=0; x < (long) image->columns; x++) |
3563 | 0 | { |
3564 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(ScaleQuantumToShort(p->red)); |
3565 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(ScaleQuantumToShort(p->green)); |
3566 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(ScaleQuantumToShort(p->blue)); |
3567 | 0 | p++; |
3568 | 0 | } |
3569 | 0 | } /* End deep JCS_RGB || JCS_YCbCr */ |
3570 | 0 | else if (jpeg_info.in_color_space == JCS_CMYK) |
3571 | 0 | { /* Start deep JCS_CMYK */ |
3572 | 0 | i=0; |
3573 | 0 | for (x=0; x < (long) image->columns; x++) |
3574 | 0 | { |
3575 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(65535U-ScaleQuantumToShort(p->red)); |
3576 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(65535U-ScaleQuantumToShort(p->green)); |
3577 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(65535U-ScaleQuantumToShort(p->blue)); |
3578 | 0 | jpeg_pixels.t.j16[i++] = (J16SAMPLE)(65535U-ScaleQuantumToShort(p->opacity)); |
3579 | 0 | p++; |
3580 | 0 | } |
3581 | 0 | } /* End deep JCS_CMYK */ |
3582 | |
|
3583 | 0 | scanline[0]=(J16SAMPROW) jpeg_pixels.t.j16; |
3584 | 0 | (void) jpeg16_write_scanlines(&jpeg_info,scanline,1); |
3585 | 0 | } |
3586 | 0 | } else |
3587 | 232k | #endif /* if defined(C_LOSSLESS_SUPPORTED) */ |
3588 | 232k | #endif /* if defined(HAVE_JPEG_ENABLE_LOSSLESS) && HAVE_JPEG_ENABLE_LOSSLESS */ |
3589 | 232k | #endif /* if defined(HAVE_JPEG16_WRITE_SCANLINES) && HAVE_JPEG16_WRITE_SCANLINES */ |
3590 | | |
3591 | 232k | #if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES |
3592 | 232k | if (jpeg_info.data_precision == 12) |
3593 | 0 | { |
3594 | 0 | J12SAMPROW |
3595 | 0 | scanline[1]; |
3596 | |
|
3597 | 0 | if (jpeg_info.in_color_space == JCS_GRAYSCALE) |
3598 | 0 | { /* Start deep JCS_GRAYSCALE */ |
3599 | 0 | if (image->is_grayscale) |
3600 | 0 | { |
3601 | 0 | i=0; |
3602 | 0 | for (x=0; x < (long) image->columns; x++) |
3603 | 0 | { |
3604 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(ScaleQuantumToShort(GetGraySample(p))/16); |
3605 | 0 | p++; |
3606 | 0 | } |
3607 | 0 | } |
3608 | 0 | else |
3609 | 0 | { |
3610 | 0 | i=0; |
3611 | 0 | for (x=0; x < (long) image->columns; x++) |
3612 | 0 | { |
3613 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(ScaleQuantumToShort(PixelIntensityToQuantum(p))/16); |
3614 | 0 | p++; |
3615 | 0 | } |
3616 | 0 | } |
3617 | 0 | } /* End deep JCS_GRAYSCALE */ |
3618 | 0 | else if ((jpeg_info.in_color_space == JCS_RGB) || |
3619 | 0 | (jpeg_info.in_color_space == JCS_YCbCr)) |
3620 | 0 | { /* Start deep JCS_RGB || JCS_YCbCr */ |
3621 | 0 | i=0; |
3622 | 0 | for (x=0; x < (long) image->columns; x++) |
3623 | 0 | { |
3624 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(ScaleQuantumToShort(p->red)/16); |
3625 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(ScaleQuantumToShort(p->green)/16); |
3626 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(ScaleQuantumToShort(p->blue)/16); |
3627 | 0 | p++; |
3628 | 0 | } |
3629 | 0 | } /* End deep JCS_RGB || JCS_YCbCr */ |
3630 | 0 | else if (jpeg_info.in_color_space == JCS_CMYK) |
3631 | 0 | { /* Start deep JCS_CMYK */ |
3632 | 0 | i=0; |
3633 | 0 | for (x=0; x < (long) image->columns; x++) |
3634 | 0 | { |
3635 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(4095U-ScaleQuantumToShort(p->red)/16); |
3636 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(4095U-ScaleQuantumToShort(p->green)/16); |
3637 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(4095U-ScaleQuantumToShort(p->blue)/16); |
3638 | 0 | jpeg_pixels.t.j12[i++] = (J12SAMPLE)(4095U-ScaleQuantumToShort(p->opacity)/16); |
3639 | 0 | p++; |
3640 | 0 | } |
3641 | 0 | } /* End deep JCS_CMYK */ |
3642 | |
|
3643 | 0 | scanline[0]=(J12SAMPROW) jpeg_pixels.t.j12; |
3644 | 0 | (void) jpeg12_write_scanlines(&jpeg_info,scanline,1); |
3645 | 0 | } else |
3646 | 232k | #endif /* if defined(HAVE_JPEG12_WRITE_SCANLINES) && HAVE_JPEG12_WRITE_SCANLINES */ |
3647 | 232k | { |
3648 | 232k | JSAMPROW |
3649 | 232k | scanline[1]; |
3650 | | |
3651 | 232k | if (jpeg_info.in_color_space == JCS_GRAYSCALE) |
3652 | 166k | { /* Start traditional JCS_GRAYSCALE */ |
3653 | 166k | if (image->is_grayscale) |
3654 | 166k | { |
3655 | 166k | i=0; |
3656 | 141M | for (x=0; x < (long) image->columns; x++) |
3657 | 141M | { |
3658 | 141M | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(GetGraySample(p))); |
3659 | 141M | p++; |
3660 | 141M | } |
3661 | 166k | } |
3662 | 0 | else |
3663 | 0 | { |
3664 | 0 | i=0; |
3665 | 0 | for (x=0; x < (long) image->columns; x++) |
3666 | 0 | { |
3667 | 0 | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(PixelIntensityToQuantum(p))); |
3668 | 0 | p++; |
3669 | 0 | } |
3670 | 0 | } |
3671 | 166k | } /* End traditional JCS_GRAYSCALE */ |
3672 | 65.8k | else if ((jpeg_info.in_color_space == JCS_RGB) || |
3673 | 0 | (jpeg_info.in_color_space == JCS_YCbCr)) |
3674 | 65.8k | { /* Start traditional JCS_RGB || JCS_YCbCr */ |
3675 | 65.8k | i=0; |
3676 | 23.8M | for (x=0; x < (long) image->columns; x++) |
3677 | 23.7M | { |
3678 | 23.7M | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(p->red)); |
3679 | 23.7M | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(p->green)); |
3680 | 23.7M | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(p->blue)); |
3681 | 23.7M | p++; |
3682 | 23.7M | } |
3683 | 65.8k | } /* End traditional JCS_RGB || JCS_YCbCr */ |
3684 | 0 | else if (jpeg_info.in_color_space == JCS_CMYK) |
3685 | 0 | { /* Start traditional JCS_CMYK */ |
3686 | 0 | i=0; |
3687 | 0 | for (x=0; x < (long) image->columns; x++) |
3688 | 0 | { |
3689 | 0 | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(MaxRGB-p->red)); |
3690 | 0 | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(MaxRGB-p->green)); |
3691 | 0 | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(MaxRGB-p->blue)); |
3692 | 0 | jpeg_pixels.t.j[i++]=(JSAMPLE)(ScaleQuantumToChar(MaxRGB-p->opacity)); |
3693 | 0 | p++; |
3694 | 0 | } |
3695 | 0 | } /* End traditional JCS_CMYK */ |
3696 | | |
3697 | 232k | scanline[0]=(JSAMPROW) jpeg_pixels.t.j; |
3698 | 232k | (void) jpeg_write_scanlines(&jpeg_info,scanline,1); |
3699 | 232k | #if !USE_LIBJPEG_PROGRESS |
3700 | 232k | if (QuantumTick(y,image->rows)) |
3701 | 79.8k | if (!MagickMonitorFormatted(y,image->rows,&image->exception, |
3702 | 79.8k | SaveImageText,image->filename, |
3703 | 79.8k | image->columns,image->rows)) |
3704 | 0 | break; |
3705 | 232k | #endif /* !USE_LIBJPEG_PROGRESS */ |
3706 | 232k | } |
3707 | 232k | } |
3708 | | |
3709 | 726 | if (image->logging) |
3710 | 0 | (void) LogMagickEvent(CoderEvent,GetMagickModule(), |
3711 | 0 | "Finishing JPEG compression"); |
3712 | 726 | jpeg_finish_compress(&jpeg_info); |
3713 | | /* |
3714 | | Free memory. |
3715 | | */ |
3716 | 726 | if (huffman_memory) |
3717 | 717 | LiberateMagickResource(MemoryResource,huffman_memory); |
3718 | 726 | client_data=FreeMagickClientData(client_data); |
3719 | 726 | jpeg_destroy_compress(&jpeg_info); |
3720 | | /* MagickFreeResourceLimitedMemory(jpeg_pixels); */ |
3721 | 726 | status &= CloseBlob(image); |
3722 | 726 | return(status); |
3723 | 726 | } |
3724 | | #endif |