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