1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % JJJJJ PPPP EEEEE GGGG %
7 % J P P E G %
8 % J PPPP EEE G GG %
9 % J J P E G G %
10 % JJJ P EEEEE GGG %
11 % %
12 % %
13 % Read/Write JPEG Image Format %
14 % %
15 % Software Design %
16 % John Cristy %
17 % July 1992 %
18 % %
19 % %
20 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 % This software is based in part on the work of the Independent JPEG Group.
37 % See ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz for copyright and
38 % licensing restrictions. Blob support contributed by Glenn Randers-Pehrson.
39 %
40 %
41 */
42
43 /*
44 Include declarations.
45 */
46 #include "MagickCore/studio.h"
47 #include "MagickCore/artifact.h"
48 #include "MagickCore/attribute.h"
49 #include "MagickCore/blob.h"
50 #include "MagickCore/blob-private.h"
51 #include "MagickCore/cache.h"
52 #include "MagickCore/color.h"
53 #include "MagickCore/colormap-private.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colorspace.h"
57 #include "MagickCore/colorspace-private.h"
58 #include "MagickCore/constitute.h"
59 #include "MagickCore/exception.h"
60 #include "MagickCore/exception-private.h"
61 #include "MagickCore/geometry.h"
62 #include "MagickCore/image.h"
63 #include "MagickCore/image-private.h"
64 #include "MagickCore/list.h"
65 #include "MagickCore/log.h"
66 #include "MagickCore/magick.h"
67 #include "MagickCore/memory_.h"
68 #include "MagickCore/memory-private.h"
69 #include "MagickCore/module.h"
70 #include "MagickCore/monitor.h"
71 #include "MagickCore/monitor-private.h"
72 #include "MagickCore/option.h"
73 #include "MagickCore/option-private.h"
74 #include "MagickCore/pixel-accessor.h"
75 #include "MagickCore/profile.h"
76 #include "MagickCore/property.h"
77 #include "MagickCore/quantum-private.h"
78 #include "MagickCore/resource_.h"
79 #include "MagickCore/semaphore.h"
80 #include "MagickCore/splay-tree.h"
81 #include "MagickCore/static.h"
82 #include "MagickCore/string_.h"
83 #include "MagickCore/string-private.h"
84 #include "MagickCore/token.h"
85 #include "MagickCore/utility.h"
86 #include "MagickCore/xml-tree.h"
87 #include "MagickCore/xml-tree-private.h"
88 #include <setjmp.h>
89 #if defined(MAGICKCORE_JPEG_DELEGATE)
90 #define JPEG_INTERNAL_OPTIONS
91 #if defined(__MINGW32__)
92 # define XMD_H 1 /* Avoid conflicting typedef for INT32 */
93 #endif
94 #undef HAVE_STDLIB_H
95 #include "jpeglib.h"
96 #include "jerror.h"
97 #endif
98
99 /*
100 Define declarations.
101 */
102 #define COMMENT_INDEX 0
103 #define ICC_INDEX 2
104 #define ICC_MARKER (JPEG_APP0+ICC_INDEX)
105 #define ICC_PROFILE "ICC_PROFILE"
106 #define IPTC_INDEX 13
107 #define IPTC_MARKER (JPEG_APP0+IPTC_INDEX)
108 #define XML_INDEX 1
109 #define XML_MARKER (JPEG_APP0+XML_INDEX)
110 #define MaxJPEGScans 1024
111
112 /*
113 Typedef declarations.
114 */
115 #if defined(MAGICKCORE_JPEG_DELEGATE)
116 typedef struct _DestinationManager
117 {
118 struct jpeg_destination_mgr
119 manager;
120
121 Image
122 *image;
123
124 JOCTET
125 *buffer;
126 } DestinationManager;
127
128 typedef struct _ErrorManager
129 {
130 jmp_buf
131 error_recovery;
132
133 Image
134 *image;
135
136 MagickBooleanType
137 finished;
138
139 StringInfo
140 *profiles[16];
141
142 ExceptionInfo
143 *exception;
144 } ErrorManager;
145
146 typedef struct _SourceManager
147 {
148 struct jpeg_source_mgr
149 manager;
150
151 Image
152 *image;
153
154 JOCTET
155 *buffer;
156
157 boolean
158 start_of_blob;
159 } SourceManager;
160 #endif
161
162 typedef struct _QuantizationTable
163 {
164 char
165 *slot,
166 *description;
167
168 size_t
169 width,
170 height;
171
172 double
173 divisor;
174
175 unsigned int
176 *levels;
177 } QuantizationTable;
178
179 /*
180 Const declarations.
181 */
182 static const char
183 xmp_namespace[] = "http://ns.adobe.com/xap/1.0/ ";
184 #define XmpNamespaceExtent 28
185
186 /*
187 Forward declarations.
188 */
189 #if defined(MAGICKCORE_JPEG_DELEGATE)
190 static MagickBooleanType
191 WriteJPEGImage(const ImageInfo *,Image *,ExceptionInfo *);
192 #endif
193
194 /*
195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 % %
197 % %
198 % %
199 % I s J P E G %
200 % %
201 % %
202 % %
203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
204 %
205 % IsJPEG() returns MagickTrue if the image format type, identified by the
206 % magick string, is JPEG.
207 %
208 % The format of the IsJPEG method is:
209 %
210 % MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
211 %
212 % A description of each parameter follows:
213 %
214 % o magick: compare image format pattern against these bytes.
215 %
216 % o length: Specifies the length of the magick string.
217 %
218 */
IsJPEG(const unsigned char * magick,const size_t length)219 static MagickBooleanType IsJPEG(const unsigned char *magick,const size_t length)
220 {
221 if (length < 3)
222 return(MagickFalse);
223 if (memcmp(magick,"\377\330\377",3) == 0)
224 return(MagickTrue);
225 return(MagickFalse);
226 }
227
228 #if defined(MAGICKCORE_JPEG_DELEGATE)
229 /*
230 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
231 % %
232 % %
233 % %
234 % R e a d J P E G I m a g e %
235 % %
236 % %
237 % %
238 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
239 %
240 % ReadJPEGImage() reads a JPEG image file and returns it. It allocates
241 % the memory necessary for the new Image structure and returns a pointer to
242 % the new image.
243 %
244 % The format of the ReadJPEGImage method is:
245 %
246 % Image *ReadJPEGImage(const ImageInfo *image_info,
247 % ExceptionInfo *exception)
248 %
249 % A description of each parameter follows:
250 %
251 % o image_info: the image info.
252 %
253 % o exception: return any errors or warnings in this structure.
254 %
255 */
256
FillInputBuffer(j_decompress_ptr cinfo)257 static boolean FillInputBuffer(j_decompress_ptr cinfo)
258 {
259 SourceManager
260 *source;
261
262 source=(SourceManager *) cinfo->src;
263 source->manager.bytes_in_buffer=(size_t) ReadBlob(source->image,
264 MagickMinBufferExtent,source->buffer);
265 if (source->manager.bytes_in_buffer == 0)
266 {
267 if (source->start_of_blob != FALSE)
268 ERREXIT(cinfo,JERR_INPUT_EMPTY);
269 WARNMS(cinfo,JWRN_JPEG_EOF);
270 source->buffer[0]=(JOCTET) 0xff;
271 source->buffer[1]=(JOCTET) JPEG_EOI;
272 source->manager.bytes_in_buffer=2;
273 }
274 source->manager.next_input_byte=source->buffer;
275 source->start_of_blob=FALSE;
276 return(TRUE);
277 }
278
GetCharacter(j_decompress_ptr jpeg_info)279 static int GetCharacter(j_decompress_ptr jpeg_info)
280 {
281 if (jpeg_info->src->bytes_in_buffer == 0)
282 {
283 (void) (*jpeg_info->src->fill_input_buffer)(jpeg_info);
284 if (jpeg_info->err->msg_code == JWRN_JPEG_EOF)
285 return(EOF);
286 }
287 jpeg_info->src->bytes_in_buffer--;
288 return((int) GETJOCTET(*jpeg_info->src->next_input_byte++));
289 }
290
InitializeSource(j_decompress_ptr cinfo)291 static void InitializeSource(j_decompress_ptr cinfo)
292 {
293 SourceManager
294 *source;
295
296 source=(SourceManager *) cinfo->src;
297 source->start_of_blob=TRUE;
298 }
299
IsITUFaxImage(const Image * image)300 static MagickBooleanType IsITUFaxImage(const Image *image)
301 {
302 const StringInfo
303 *profile;
304
305 const unsigned char
306 *datum;
307
308 profile=GetImageProfile(image,"8bim");
309 if (profile == (const StringInfo *) NULL)
310 return(MagickFalse);
311 if (GetStringInfoLength(profile) < 5)
312 return(MagickFalse);
313 datum=GetStringInfoDatum(profile);
314 if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
315 (datum[3] == 0x41) && (datum[4] == 0x58))
316 return(MagickTrue);
317 return(MagickFalse);
318 }
319
JPEGErrorHandler(j_common_ptr jpeg_info)320 static void JPEGErrorHandler(j_common_ptr jpeg_info)
321 {
322 char
323 message[JMSG_LENGTH_MAX];
324
325 ErrorManager
326 *error_manager;
327
328 ExceptionInfo
329 *exception;
330
331 Image
332 *image;
333
334 *message='\0';
335 error_manager=(ErrorManager *) jpeg_info->client_data;
336 image=error_manager->image;
337 exception=error_manager->exception;
338 (jpeg_info->err->format_message)(jpeg_info,message);
339 if (image->debug != MagickFalse)
340 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
341 "[%s] JPEG Trace: \"%s\"",image->filename,message);
342 if (error_manager->finished != MagickFalse)
343 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageWarning,
344 (char *) message,"`%s'",image->filename);
345 else
346 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
347 (char *) message,"`%s'",image->filename);
348 longjmp(error_manager->error_recovery,1);
349 }
350
JPEGProgressHandler(j_common_ptr jpeg_info)351 static void JPEGProgressHandler(j_common_ptr jpeg_info)
352 {
353 ErrorManager
354 *error_manager;
355
356 ExceptionInfo
357 *exception;
358
359 Image
360 *image;
361
362 error_manager=(ErrorManager *) jpeg_info->client_data;
363 image=error_manager->image;
364 exception=error_manager->exception;
365 if (jpeg_info->is_decompressor == 0)
366 return;
367 if (((j_decompress_ptr) jpeg_info)->input_scan_number < MaxJPEGScans)
368 return;
369 (void) ThrowMagickException(exception,GetMagickModule(),CorruptImageError,
370 "too many scans","`%s'",image->filename);
371 longjmp(error_manager->error_recovery,1);
372 }
373
JPEGWarningHandler(j_common_ptr jpeg_info,int level)374 static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
375 {
376 #define JPEGExcessiveWarnings 1000
377
378 char
379 message[JMSG_LENGTH_MAX];
380
381 ErrorManager
382 *error_manager;
383
384 ExceptionInfo
385 *exception;
386
387 Image
388 *image;
389
390 *message='\0';
391 error_manager=(ErrorManager *) jpeg_info->client_data;
392 exception=error_manager->exception;
393 image=error_manager->image;
394 if (level < 0)
395 {
396 /*
397 Process warning message.
398 */
399 (jpeg_info->err->format_message)(jpeg_info,message);
400 if (jpeg_info->err->num_warnings++ < JPEGExcessiveWarnings)
401 ThrowBinaryException(CorruptImageWarning,(char *) message,
402 image->filename);
403 }
404 else
405 if ((image->debug != MagickFalse) &&
406 (level >= jpeg_info->err->trace_level))
407 {
408 /*
409 Process trace message.
410 */
411 (jpeg_info->err->format_message)(jpeg_info,message);
412 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
413 "[%s] JPEG Trace: \"%s\"",image->filename,message);
414 }
415 return(MagickTrue);
416 }
417
ReadProfileData(j_decompress_ptr jpeg_info,const size_t index,const size_t length)418 static boolean ReadProfileData(j_decompress_ptr jpeg_info,const size_t index,
419 const size_t length)
420 {
421 ErrorManager
422 *error_manager;
423
424 ExceptionInfo
425 *exception;
426
427 Image
428 *image;
429
430 unsigned char
431 *p;
432
433 ssize_t
434 i;
435
436 error_manager=(ErrorManager *) jpeg_info->client_data;
437 exception=error_manager->exception;
438 image=error_manager->image;
439 if (error_manager->profiles[index] == (StringInfo *) NULL)
440 {
441 error_manager->profiles[index]=BlobToStringInfo((const void *) NULL,
442 length);
443 if (error_manager->profiles[index] == (StringInfo *) NULL)
444 {
445 (void) ThrowMagickException(exception,GetMagickModule(),
446 ResourceLimitError,"MemoryAllocationFailed","`%s'",
447 image->filename);
448 return(FALSE);
449 }
450 p=GetStringInfoDatum(error_manager->profiles[index]);
451 }
452 else
453 {
454 size_t
455 current_length;
456
457 current_length=GetStringInfoLength(error_manager->profiles[index]);
458 SetStringInfoLength(error_manager->profiles[index],current_length+
459 length);
460 p=GetStringInfoDatum(error_manager->profiles[index])+current_length;
461 }
462 for (i=0; i < (ssize_t) length; i++)
463 {
464 int
465 c;
466
467 c=GetCharacter(jpeg_info);
468 if (c == EOF)
469 break;
470 *p++=(unsigned char) c;
471 }
472 if (i != (ssize_t) length)
473 {
474 (void) ThrowMagickException(exception,GetMagickModule(),
475 CorruptImageError,"InsufficientImageDataInFile","`%s'",
476 image->filename);
477 return(FALSE);
478 }
479 *p='\0';
480 if (image->debug != MagickFalse)
481 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
482 "Profile[%.20g]: %.20g bytes",(double) index,(double) length);
483 return(TRUE);
484 }
485
ReadComment(j_decompress_ptr jpeg_info)486 static boolean ReadComment(j_decompress_ptr jpeg_info)
487 {
488 #define GetProfileLength(jpeg_info,length) \
489 { \
490 int \
491 c[2]; \
492 \
493 length=0; \
494 c[0]=GetCharacter(jpeg_info); \
495 c[1]=GetCharacter(jpeg_info); \
496 if ((c[0] >= 0) && (c[1] >= 0)) \
497 length=(size_t) ((c[0] << 8) | c[1]); \
498 }
499
500 size_t
501 length;
502
503 /*
504 Determine length of comment.
505 */
506 GetProfileLength(jpeg_info,length);
507 if (length <= 2)
508 return(TRUE);
509 length-=2;
510 return(ReadProfileData(jpeg_info,COMMENT_INDEX,length));
511 }
512
ReadICCProfile(j_decompress_ptr jpeg_info)513 static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
514 {
515 char
516 magick[13];
517
518 ssize_t
519 i;
520
521 size_t
522 length;
523
524 /*
525 Read color profile.
526 */
527 GetProfileLength(jpeg_info,length);
528 if (length <= 2)
529 return(TRUE);
530 length-=2;
531 if (length <= 14)
532 {
533 while (length-- > 0)
534 if (GetCharacter(jpeg_info) == EOF)
535 break;
536 return(TRUE);
537 }
538 for (i=0; i < 12; i++)
539 magick[i]=(char) GetCharacter(jpeg_info);
540 magick[i]='\0';
541 if (LocaleCompare(magick,ICC_PROFILE) != 0)
542 {
543 /*
544 Not a ICC profile, return.
545 */
546 for (i=0; i < (ssize_t) (length-12); i++)
547 if (GetCharacter(jpeg_info) == EOF)
548 break;
549 return(TRUE);
550 }
551 (void) GetCharacter(jpeg_info); /* id */
552 (void) GetCharacter(jpeg_info); /* markers */
553 length-=14;
554 return(ReadProfileData(jpeg_info,ICC_INDEX,length));
555 }
556
ReadIPTCProfile(j_decompress_ptr jpeg_info)557 static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
558 {
559 char
560 magick[MagickPathExtent];
561
562 ssize_t
563 i;
564
565 size_t
566 length;
567
568 /*
569 Determine length of binary data stored here.
570 */
571 GetProfileLength(jpeg_info,length);
572 if (length <= 2)
573 return(TRUE);
574 length-=2;
575 if (length <= 14)
576 {
577 while (length-- > 0)
578 if (GetCharacter(jpeg_info) == EOF)
579 break;
580 return(TRUE);
581 }
582 /*
583 Validate that this was written as a Photoshop resource format slug.
584 */
585 for (i=0; i < 10; i++)
586 magick[i]=(char) GetCharacter(jpeg_info);
587 magick[10]='\0';
588 length-=10;
589 if (length <= 10)
590 return(TRUE);
591 if (LocaleCompare(magick,"Photoshop ") != 0)
592 {
593 /*
594 Not a IPTC profile, return.
595 */
596 for (i=0; i < (ssize_t) length; i++)
597 if (GetCharacter(jpeg_info) == EOF)
598 break;
599 return(TRUE);
600 }
601 /*
602 Remove the version number.
603 */
604 for (i=0; i < 4; i++)
605 if (GetCharacter(jpeg_info) == EOF)
606 break;
607 if (length <= 11)
608 return(TRUE);
609 length-=4;
610 return(ReadProfileData(jpeg_info,IPTC_INDEX,length));
611 }
612
ReadProfile(j_decompress_ptr jpeg_info)613 static boolean ReadProfile(j_decompress_ptr jpeg_info)
614 {
615 int
616 marker;
617
618 size_t
619 length;
620
621 /*
622 Read generic profile.
623 */
624 GetProfileLength(jpeg_info,length);
625 if (length <= 2)
626 return(TRUE);
627 length-=2;
628 marker=jpeg_info->unread_marker-JPEG_APP0;
629 return(ReadProfileData(jpeg_info,marker,length));
630 }
631
ReadXmlProfile(j_decompress_ptr jpeg_info)632 static boolean ReadXmlProfile(j_decompress_ptr jpeg_info)
633 {
634 ErrorManager
635 *error_manager;
636
637 ExceptionInfo
638 *exception;
639
640 Image
641 *image;
642
643 MagickBooleanType
644 status;
645
646 unsigned char
647 *p;
648
649 size_t
650 length;
651
652 StringInfo
653 *profile;
654
655 GetProfileLength(jpeg_info,length);
656 if (length <= 2)
657 return(TRUE);
658 length-=2;
659 if (ReadProfileData(jpeg_info,XML_INDEX,length) == FALSE)
660 return(FALSE);
661 error_manager=(ErrorManager *) jpeg_info->client_data;
662 exception=error_manager->exception;
663 image=error_manager->image;
664 profile=error_manager->profiles[XML_INDEX];
665 p=GetStringInfoDatum(profile);
666 length=GetStringInfoLength(profile);
667 status=MagickTrue;
668 if ((length > XmpNamespaceExtent) &&
669 (LocaleNCompare((char *) p,xmp_namespace,XmpNamespaceExtent-1) == 0))
670 {
671 ssize_t
672 j;
673
674 /*
675 Extract namespace from XMP profile.
676 */
677 p=GetStringInfoDatum(profile)+XmpNamespaceExtent;
678 for (j=XmpNamespaceExtent; j < (ssize_t) length; j++)
679 {
680 if (*p == '\0')
681 break;
682 p++;
683 }
684 if (j < (ssize_t) length)
685 (void) DestroyStringInfo(SplitStringInfo(profile,(size_t) (j+1)));
686 status=SetImageProfile(image,"xmp",profile,exception);
687 }
688 else
689 if (length > 4)
690 {
691 if ((LocaleNCompare((char *) p,"exif",4) == 0) ||
692 (LocaleNCompare((char *) p,"MM",2) == 0) ||
693 (LocaleNCompare((char *) p,"II",2) == 0))
694 status=SetImageProfile(image,"exif",profile,exception);
695 }
696 else
697 status=SetImageProfile(image,"app1",profile,exception);
698 error_manager->profiles[XML_INDEX]=DestroyStringInfo(
699 error_manager->profiles[XML_INDEX]);
700 return(status != MagickFalse ? TRUE : FALSE);
701 }
702
SkipInputData(j_decompress_ptr cinfo,long number_bytes)703 static void SkipInputData(j_decompress_ptr cinfo,long number_bytes)
704 {
705 SourceManager
706 *source;
707
708 if (number_bytes <= 0)
709 return;
710 source=(SourceManager *) cinfo->src;
711 while (number_bytes > (long) source->manager.bytes_in_buffer)
712 {
713 number_bytes-=(long) source->manager.bytes_in_buffer;
714 (void) FillInputBuffer(cinfo);
715 }
716 source->manager.next_input_byte+=number_bytes;
717 source->manager.bytes_in_buffer-=number_bytes;
718 }
719
TerminateSource(j_decompress_ptr cinfo)720 static void TerminateSource(j_decompress_ptr cinfo)
721 {
722 (void) cinfo;
723 }
724
JPEGSourceManager(j_decompress_ptr cinfo,Image * image)725 static void JPEGSourceManager(j_decompress_ptr cinfo,Image *image)
726 {
727 SourceManager
728 *source;
729
730 cinfo->src=(struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)
731 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(SourceManager));
732 source=(SourceManager *) cinfo->src;
733 source->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
734 ((j_common_ptr) cinfo,JPOOL_IMAGE,MagickMinBufferExtent*sizeof(JOCTET));
735 source=(SourceManager *) cinfo->src;
736 source->manager.init_source=InitializeSource;
737 source->manager.fill_input_buffer=FillInputBuffer;
738 source->manager.skip_input_data=SkipInputData;
739 source->manager.resync_to_restart=jpeg_resync_to_restart;
740 source->manager.term_source=TerminateSource;
741 source->manager.bytes_in_buffer=0;
742 source->manager.next_input_byte=NULL;
743 source->image=image;
744 }
745
JPEGSetImageQuality(struct jpeg_decompress_struct * jpeg_info,Image * image)746 static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
747 Image *image)
748 {
749 image->quality=UndefinedCompressionQuality;
750 #if defined(D_PROGRESSIVE_SUPPORTED)
751 if (image->compression == LosslessJPEGCompression)
752 {
753 image->quality=100;
754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
755 "Quality: 100 (lossless)");
756 }
757 else
758 #endif
759 {
760 ssize_t
761 j,
762 qvalue,
763 sum;
764
765 ssize_t
766 i;
767
768 /*
769 Determine the JPEG compression quality from the quantization tables.
770 */
771 sum=0;
772 for (i=0; i < NUM_QUANT_TBLS; i++)
773 {
774 if (jpeg_info->quant_tbl_ptrs[i] != NULL)
775 for (j=0; j < DCTSIZE2; j++)
776 sum+=jpeg_info->quant_tbl_ptrs[i]->quantval[j];
777 }
778 if ((jpeg_info->quant_tbl_ptrs[0] != NULL) &&
779 (jpeg_info->quant_tbl_ptrs[1] != NULL))
780 {
781 ssize_t
782 hash[101] =
783 {
784 1020, 1015, 932, 848, 780, 735, 702, 679, 660, 645,
785 632, 623, 613, 607, 600, 594, 589, 585, 581, 571,
786 555, 542, 529, 514, 494, 474, 457, 439, 424, 410,
787 397, 386, 373, 364, 351, 341, 334, 324, 317, 309,
788 299, 294, 287, 279, 274, 267, 262, 257, 251, 247,
789 243, 237, 232, 227, 222, 217, 213, 207, 202, 198,
790 192, 188, 183, 177, 173, 168, 163, 157, 153, 148,
791 143, 139, 132, 128, 125, 119, 115, 108, 104, 99,
792 94, 90, 84, 79, 74, 70, 64, 59, 55, 49,
793 45, 40, 34, 30, 25, 20, 15, 11, 6, 4,
794 0
795 },
796 sums[101] =
797 {
798 32640, 32635, 32266, 31495, 30665, 29804, 29146, 28599, 28104,
799 27670, 27225, 26725, 26210, 25716, 25240, 24789, 24373, 23946,
800 23572, 22846, 21801, 20842, 19949, 19121, 18386, 17651, 16998,
801 16349, 15800, 15247, 14783, 14321, 13859, 13535, 13081, 12702,
802 12423, 12056, 11779, 11513, 11135, 10955, 10676, 10392, 10208,
803 9928, 9747, 9564, 9369, 9193, 9017, 8822, 8639, 8458,
804 8270, 8084, 7896, 7710, 7527, 7347, 7156, 6977, 6788,
805 6607, 6422, 6236, 6054, 5867, 5684, 5495, 5305, 5128,
806 4945, 4751, 4638, 4442, 4248, 4065, 3888, 3698, 3509,
807 3326, 3139, 2957, 2775, 2586, 2405, 2216, 2037, 1846,
808 1666, 1483, 1297, 1109, 927, 735, 554, 375, 201,
809 128, 0
810 };
811
812 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
813 jpeg_info->quant_tbl_ptrs[0]->quantval[53]+
814 jpeg_info->quant_tbl_ptrs[1]->quantval[0]+
815 jpeg_info->quant_tbl_ptrs[1]->quantval[DCTSIZE2-1]);
816 for (i=0; i < 100; i++)
817 {
818 if ((qvalue < hash[i]) && (sum < sums[i]))
819 continue;
820 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
821 image->quality=(size_t) i+1;
822 if (image->debug != MagickFalse)
823 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
824 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
825 (sum <= sums[i]) ? "exact" : "approximate");
826 break;
827 }
828 }
829 else
830 if (jpeg_info->quant_tbl_ptrs[0] != NULL)
831 {
832 ssize_t
833 hash[101] =
834 {
835 510, 505, 422, 380, 355, 338, 326, 318, 311, 305,
836 300, 297, 293, 291, 288, 286, 284, 283, 281, 280,
837 279, 278, 277, 273, 262, 251, 243, 233, 225, 218,
838 211, 205, 198, 193, 186, 181, 177, 172, 168, 164,
839 158, 156, 152, 148, 145, 142, 139, 136, 133, 131,
840 129, 126, 123, 120, 118, 115, 113, 110, 107, 105,
841 102, 100, 97, 94, 92, 89, 87, 83, 81, 79,
842 76, 74, 70, 68, 66, 63, 61, 57, 55, 52,
843 50, 48, 44, 42, 39, 37, 34, 31, 29, 26,
844 24, 21, 18, 16, 13, 11, 8, 6, 3, 2,
845 0
846 },
847 sums[101] =
848 {
849 16320, 16315, 15946, 15277, 14655, 14073, 13623, 13230, 12859,
850 12560, 12240, 11861, 11456, 11081, 10714, 10360, 10027, 9679,
851 9368, 9056, 8680, 8331, 7995, 7668, 7376, 7084, 6823,
852 6562, 6345, 6125, 5939, 5756, 5571, 5421, 5240, 5086,
853 4976, 4829, 4719, 4616, 4463, 4393, 4280, 4166, 4092,
854 3980, 3909, 3835, 3755, 3688, 3621, 3541, 3467, 3396,
855 3323, 3247, 3170, 3096, 3021, 2952, 2874, 2804, 2727,
856 2657, 2583, 2509, 2437, 2362, 2290, 2211, 2136, 2068,
857 1996, 1915, 1858, 1773, 1692, 1620, 1552, 1477, 1398,
858 1326, 1251, 1179, 1109, 1031, 961, 884, 814, 736,
859 667, 592, 518, 441, 369, 292, 221, 151, 86,
860 64, 0
861 };
862
863 qvalue=(ssize_t) (jpeg_info->quant_tbl_ptrs[0]->quantval[2]+
864 jpeg_info->quant_tbl_ptrs[0]->quantval[53]);
865 for (i=0; i < 100; i++)
866 {
867 if ((qvalue < hash[i]) && (sum < sums[i]))
868 continue;
869 if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
870 image->quality=(size_t)i+1;
871 if (image->debug != MagickFalse)
872 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
873 "Quality: %.20g (%s)",(double) i+1,(qvalue <= hash[i]) &&
874 (sum <= sums[i]) ? "exact" : "approximate");
875 break;
876 }
877 }
878 }
879 }
880
JPEGSetImageSamplingFactor(struct jpeg_decompress_struct * jpeg_info,Image * image,ExceptionInfo * exception)881 static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info, Image *image,ExceptionInfo *exception)
882 {
883 char
884 sampling_factor[MagickPathExtent];
885
886 switch (jpeg_info->out_color_space)
887 {
888 case JCS_CMYK:
889 {
890 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: CMYK");
891 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
892 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
893 jpeg_info->comp_info[0].v_samp_factor,
894 jpeg_info->comp_info[1].h_samp_factor,
895 jpeg_info->comp_info[1].v_samp_factor,
896 jpeg_info->comp_info[2].h_samp_factor,
897 jpeg_info->comp_info[2].v_samp_factor,
898 jpeg_info->comp_info[3].h_samp_factor,
899 jpeg_info->comp_info[3].v_samp_factor);
900 break;
901 }
902 case JCS_GRAYSCALE:
903 {
904 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
905 "Colorspace: GRAYSCALE");
906 (void) FormatLocaleString(sampling_factor,MagickPathExtent,"%dx%d",
907 jpeg_info->comp_info[0].h_samp_factor,
908 jpeg_info->comp_info[0].v_samp_factor);
909 break;
910 }
911 case JCS_RGB:
912 {
913 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: RGB");
914 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
915 "%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
916 jpeg_info->comp_info[0].v_samp_factor,
917 jpeg_info->comp_info[1].h_samp_factor,
918 jpeg_info->comp_info[1].v_samp_factor,
919 jpeg_info->comp_info[2].h_samp_factor,
920 jpeg_info->comp_info[2].v_samp_factor);
921 break;
922 }
923 default:
924 {
925 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
926 jpeg_info->out_color_space);
927 (void) FormatLocaleString(sampling_factor,MagickPathExtent,
928 "%dx%d,%dx%d,%dx%d,%dx%d",jpeg_info->comp_info[0].h_samp_factor,
929 jpeg_info->comp_info[0].v_samp_factor,
930 jpeg_info->comp_info[1].h_samp_factor,
931 jpeg_info->comp_info[1].v_samp_factor,
932 jpeg_info->comp_info[2].h_samp_factor,
933 jpeg_info->comp_info[2].v_samp_factor,
934 jpeg_info->comp_info[3].h_samp_factor,
935 jpeg_info->comp_info[3].v_samp_factor);
936 break;
937 }
938 }
939 (void) SetImageProperty(image,"jpeg:sampling-factor",sampling_factor,
940 exception);
941 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Sampling Factors: %s",
942 sampling_factor);
943 }
944
JPEGDestroyDecompress(j_decompress_ptr jpeg_info)945 static void JPEGDestroyDecompress(j_decompress_ptr jpeg_info)
946 {
947 ErrorManager
948 *error_manager;
949
950 size_t
951 i;
952
953 error_manager=(ErrorManager *) jpeg_info->client_data;
954 for (i=0; i < 16; i++)
955 {
956 if (error_manager->profiles[i] != (StringInfo *) NULL)
957 error_manager->profiles[i]=DestroyStringInfo(error_manager->profiles[i]);
958 }
959 jpeg_destroy_decompress(jpeg_info);
960 }
961
JPEGSetImageProfiles(ErrorManager * error_manager)962 static MagickBooleanType JPEGSetImageProfiles(ErrorManager *error_manager)
963 {
964 ExceptionInfo
965 *exception;
966
967 Image
968 *image;
969
970 MagickBooleanType
971 status;
972
973 unsigned char
974 *p;
975
976 ssize_t
977 i;
978
979 StringInfo
980 *profile;
981
982 exception=error_manager->exception;
983 image=error_manager->image;
984 status=MagickTrue;
985 for (i=0; i < 16; i++)
986 {
987 profile=error_manager->profiles[i];
988 if (profile == (StringInfo *) NULL)
989 continue;
990 switch (i)
991 {
992 case COMMENT_INDEX:
993 {
994 p=GetStringInfoDatum(profile);
995 status=SetImageProperty(image,"comment",(const char *) p,exception);
996 break;
997 }
998 case ICC_INDEX:
999 {
1000 status=SetImageProfile(image,"icc",profile,exception);
1001 break;
1002 }
1003 case IPTC_INDEX:
1004 {
1005 /*
1006 The IPTC profile is actually an 8bim.
1007 */
1008 status=SetImageProfile(image,"8bim",profile,exception);
1009 break;
1010 }
1011 default:
1012 {
1013 char
1014 name[6];
1015
1016 (void) FormatLocaleString(name,sizeof(name),"APP%d",(int) i);
1017 status=SetImageProfile(image,name,profile,exception);
1018 break;
1019 }
1020 }
1021 error_manager->profiles[i]=DestroyStringInfo(error_manager->profiles[i]);
1022 if (status == MagickFalse)
1023 break;
1024 }
1025 return(status);
1026 }
1027
ReadJPEGImage_(const ImageInfo * image_info,struct jpeg_decompress_struct * jpeg_info,ExceptionInfo * exception)1028 static Image *ReadJPEGImage_(const ImageInfo *image_info,
1029 struct jpeg_decompress_struct *jpeg_info,ExceptionInfo *exception)
1030 {
1031 char
1032 value[MagickPathExtent];
1033
1034 const char
1035 *dct_method,
1036 *option;
1037
1038 ErrorManager
1039 error_manager;
1040
1041 Image
1042 *image;
1043
1044 JSAMPLE
1045 *volatile jpeg_pixels;
1046
1047 JSAMPROW
1048 scanline[1];
1049
1050 MagickBooleanType
1051 debug,
1052 status;
1053
1054 MagickSizeType
1055 number_pixels;
1056
1057 MemoryInfo
1058 *memory_info;
1059
1060 Quantum
1061 index;
1062
1063 ssize_t
1064 i;
1065
1066 struct jpeg_error_mgr
1067 jpeg_error;
1068
1069 struct jpeg_progress_mgr
1070 jpeg_progress;
1071
1072 JSAMPLE
1073 *p;
1074
1075 size_t
1076 units;
1077
1078 ssize_t
1079 y;
1080
1081 /*
1082 Open image file.
1083 */
1084 assert(image_info != (const ImageInfo *) NULL);
1085 assert(image_info->signature == MagickCoreSignature);
1086 if (image_info->debug != MagickFalse)
1087 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
1088 image_info->filename);
1089 assert(exception != (ExceptionInfo *) NULL);
1090 assert(exception->signature == MagickCoreSignature);
1091 debug=IsEventLogging();
1092 (void) debug;
1093 image=AcquireImage(image_info,exception);
1094 status=OpenBlob(image_info,image,ReadBinaryBlobMode,exception);
1095 if (status == MagickFalse)
1096 {
1097 image=DestroyImageList(image);
1098 return((Image *) NULL);
1099 }
1100 /*
1101 Verify that file size large enough to contain a JPEG datastream.
1102 */
1103 if (GetBlobSize(image) < 107)
1104 ThrowReaderException(CorruptImageError,"InsufficientImageDataInFile");
1105 /*
1106 Initialize JPEG parameters.
1107 */
1108 (void) memset(&error_manager,0,sizeof(error_manager));
1109 (void) memset(jpeg_info,0,sizeof(*jpeg_info));
1110 (void) memset(&jpeg_error,0,sizeof(jpeg_error));
1111 (void) memset(&jpeg_progress,0,sizeof(jpeg_progress));
1112 jpeg_info->err=jpeg_std_error(&jpeg_error);
1113 jpeg_info->err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
1114 jpeg_info->err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
1115 memory_info=(MemoryInfo *) NULL;
1116 error_manager.exception=exception;
1117 error_manager.image=image;
1118 if (setjmp(error_manager.error_recovery) != 0)
1119 {
1120 JPEGDestroyDecompress(jpeg_info);
1121 (void) CloseBlob(image);
1122 if (exception->severity < ErrorException)
1123 return(GetFirstImageInList(image));
1124 return(DestroyImage(image));
1125 }
1126 jpeg_info->client_data=(void *) &error_manager;
1127 jpeg_create_decompress(jpeg_info);
1128 if (GetMaxMemoryRequest() != ~0UL)
1129 jpeg_info->mem->max_memory_to_use=(long) GetMaxMemoryRequest();
1130 jpeg_progress.progress_monitor=(void (*)(j_common_ptr)) JPEGProgressHandler;
1131 jpeg_info->progress=(&jpeg_progress);
1132 JPEGSourceManager(jpeg_info,image);
1133 jpeg_set_marker_processor(jpeg_info,JPEG_COM,ReadComment);
1134 option=GetImageOption(image_info,"profile:skip");
1135 if (IsOptionMember("ICC",option) == MagickFalse)
1136 jpeg_set_marker_processor(jpeg_info,ICC_MARKER,ReadICCProfile);
1137 if (IsOptionMember("IPTC",option) == MagickFalse)
1138 jpeg_set_marker_processor(jpeg_info,IPTC_MARKER,ReadIPTCProfile);
1139 if (IsOptionMember("APP",option) == MagickFalse)
1140 jpeg_set_marker_processor(jpeg_info,XML_MARKER,ReadXmlProfile);
1141 for (i=3; i < 16; i++)
1142 /* APP14 is ignored because this will change the colors of the image */
1143 if (i != IPTC_INDEX && i != 14)
1144 if (IsOptionMember("APP",option) == MagickFalse)
1145 jpeg_set_marker_processor(jpeg_info,(int) (JPEG_APP0+i),ReadProfile);
1146 i=(ssize_t) jpeg_read_header(jpeg_info,TRUE);
1147 if (IsYCbCrCompatibleColorspace(image_info->colorspace) != MagickFalse)
1148 jpeg_info->out_color_space=JCS_YCbCr;
1149 /*
1150 Set image resolution.
1151 */
1152 units=0;
1153 if ((jpeg_info->saw_JFIF_marker != 0) && (jpeg_info->X_density != 1) &&
1154 (jpeg_info->Y_density != 1))
1155 {
1156 image->resolution.x=(double) jpeg_info->X_density;
1157 image->resolution.y=(double) jpeg_info->Y_density;
1158 units=(size_t) jpeg_info->density_unit;
1159 }
1160 if (units == 1)
1161 image->units=PixelsPerInchResolution;
1162 if (units == 2)
1163 image->units=PixelsPerCentimeterResolution;
1164 number_pixels=(MagickSizeType) image->columns*image->rows;
1165 option=GetImageOption(image_info,"jpeg:size");
1166 if ((option != (const char *) NULL) &&
1167 (jpeg_info->out_color_space != JCS_YCbCr))
1168 {
1169 double
1170 scale_factor;
1171
1172 GeometryInfo
1173 geometry_info;
1174
1175 MagickStatusType
1176 flags;
1177
1178 /*
1179 Scale the image.
1180 */
1181 flags=ParseGeometry(option,&geometry_info);
1182 if ((flags & SigmaValue) == 0)
1183 geometry_info.sigma=geometry_info.rho;
1184 jpeg_calc_output_dimensions(jpeg_info);
1185 image->magick_columns=jpeg_info->output_width;
1186 image->magick_rows=jpeg_info->output_height;
1187 scale_factor=1.0;
1188 if (geometry_info.rho != 0.0)
1189 scale_factor=jpeg_info->output_width/geometry_info.rho;
1190 if ((geometry_info.sigma != 0.0) &&
1191 (scale_factor > (jpeg_info->output_height/geometry_info.sigma)))
1192 scale_factor=jpeg_info->output_height/geometry_info.sigma;
1193 jpeg_info->scale_num=1U;
1194 jpeg_info->scale_denom=(unsigned int) scale_factor;
1195 jpeg_calc_output_dimensions(jpeg_info);
1196 if (image->debug != MagickFalse)
1197 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1198 "Scale factor: %.20g",(double) scale_factor);
1199 }
1200 #if (JPEG_LIB_VERSION >= 61) && defined(D_PROGRESSIVE_SUPPORTED)
1201 #if defined(D_LOSSLESS_SUPPORTED)
1202 image->interlace=jpeg_info->process == JPROC_PROGRESSIVE ?
1203 JPEGInterlace : NoInterlace;
1204 image->compression=jpeg_info->process == JPROC_LOSSLESS ?
1205 LosslessJPEGCompression : JPEGCompression;
1206 if (jpeg_info->data_precision > 8)
1207 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1208 "12-bit JPEG not supported. Reducing pixel data to 8 bits","`%s'",
1209 image->filename);
1210 if (jpeg_info->data_precision == 16)
1211 jpeg_info->data_precision=12;
1212 #else
1213 image->interlace=jpeg_info->progressive_mode != 0 ? JPEGInterlace :
1214 NoInterlace;
1215 image->compression=JPEGCompression;
1216 #endif
1217 #else
1218 image->compression=JPEGCompression;
1219 image->interlace=JPEGInterlace;
1220 #endif
1221 option=GetImageOption(image_info,"jpeg:colors");
1222 if (option != (const char *) NULL)
1223 {
1224 /*
1225 Let the JPEG library quantize the image.
1226 */
1227 jpeg_info->quantize_colors=TRUE;
1228 jpeg_info->desired_number_of_colors=(int) StringToUnsignedLong(option);
1229 }
1230 option=GetImageOption(image_info,"jpeg:block-smoothing");
1231 if (option != (const char *) NULL)
1232 jpeg_info->do_block_smoothing=IsStringTrue(option) != MagickFalse ? TRUE :
1233 FALSE;
1234 dct_method=GetImageOption(image_info,"jpeg:dct-method");
1235 if (dct_method != (const char *) NULL)
1236 switch (*dct_method)
1237 {
1238 case 'D':
1239 case 'd':
1240 {
1241 if (LocaleCompare(dct_method,"default") == 0)
1242 jpeg_info->dct_method=JDCT_DEFAULT;
1243 break;
1244 }
1245 case 'F':
1246 case 'f':
1247 {
1248 if (LocaleCompare(dct_method,"fastest") == 0)
1249 jpeg_info->dct_method=JDCT_FASTEST;
1250 if (LocaleCompare(dct_method,"float") == 0)
1251 jpeg_info->dct_method=JDCT_FLOAT;
1252 break;
1253 }
1254 case 'I':
1255 case 'i':
1256 {
1257 if (LocaleCompare(dct_method,"ifast") == 0)
1258 jpeg_info->dct_method=JDCT_IFAST;
1259 if (LocaleCompare(dct_method,"islow") == 0)
1260 jpeg_info->dct_method=JDCT_ISLOW;
1261 break;
1262 }
1263 }
1264 option=GetImageOption(image_info,"jpeg:fancy-upsampling");
1265 if (option != (const char *) NULL)
1266 jpeg_info->do_fancy_upsampling=IsStringTrue(option) != MagickFalse ? TRUE :
1267 FALSE;
1268 jpeg_calc_output_dimensions(jpeg_info);
1269 image->columns=jpeg_info->output_width;
1270 image->rows=jpeg_info->output_height;
1271 image->depth=(size_t) jpeg_info->data_precision;
1272 switch (jpeg_info->out_color_space)
1273 {
1274 case JCS_RGB:
1275 default:
1276 {
1277 (void) SetImageColorspace(image,sRGBColorspace,exception);
1278 break;
1279 }
1280 case JCS_GRAYSCALE:
1281 {
1282 (void) SetImageColorspace(image,GRAYColorspace,exception);
1283 break;
1284 }
1285 case JCS_YCbCr:
1286 {
1287 (void) SetImageColorspace(image,YCbCrColorspace,exception);
1288 break;
1289 }
1290 case JCS_CMYK:
1291 {
1292 (void) SetImageColorspace(image,CMYKColorspace,exception);
1293 break;
1294 }
1295 }
1296 if (IsITUFaxImage(image) != MagickFalse)
1297 {
1298 (void) SetImageColorspace(image,LabColorspace,exception);
1299 jpeg_info->out_color_space=JCS_YCbCr;
1300 }
1301 option=GetImageOption(image_info,"jpeg:colors");
1302 if (option != (const char *) NULL)
1303 if (AcquireImageColormap(image,StringToUnsignedLong(option),exception) == MagickFalse)
1304 {
1305 JPEGDestroyDecompress(jpeg_info);
1306 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1307 }
1308 if ((jpeg_info->output_components == 1) && (jpeg_info->quantize_colors == 0))
1309 {
1310 size_t
1311 colors;
1312
1313 colors=(size_t) GetQuantumRange(image->depth)+1;
1314 if (AcquireImageColormap(image,colors,exception) == MagickFalse)
1315 {
1316 JPEGDestroyDecompress(jpeg_info);
1317 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1318 }
1319 }
1320 if (image->debug != MagickFalse)
1321 {
1322 if (image->interlace != NoInterlace)
1323 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1324 "Interlace: progressive");
1325 else
1326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
1327 "Interlace: nonprogressive");
1328 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Data precision: %d",
1329 (int) jpeg_info->data_precision);
1330 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Geometry: %dx%d",
1331 (int) jpeg_info->output_width,(int) jpeg_info->output_height);
1332 }
1333 JPEGSetImageQuality(jpeg_info,image);
1334 JPEGSetImageSamplingFactor(jpeg_info,image,exception);
1335 (void) FormatLocaleString(value,MagickPathExtent,"%.20g",(double)
1336 jpeg_info->out_color_space);
1337 (void) SetImageProperty(image,"jpeg:colorspace",value,exception);
1338 #if defined(D_ARITH_CODING_SUPPORTED)
1339 if (jpeg_info->arith_code == TRUE)
1340 (void) SetImageProperty(image,"jpeg:coding","arithmetic",exception);
1341 #endif
1342 if (JPEGSetImageProfiles(&error_manager) == MagickFalse)
1343 {
1344 JPEGDestroyDecompress(jpeg_info);
1345 return(DestroyImageList(image));
1346 }
1347 if (image_info->ping != MagickFalse)
1348 {
1349 JPEGDestroyDecompress(jpeg_info);
1350 (void) CloseBlob(image);
1351 return(GetFirstImageInList(image));
1352 }
1353 status=SetImageExtent(image,image->columns,image->rows,exception);
1354 if (status == MagickFalse)
1355 {
1356 JPEGDestroyDecompress(jpeg_info);
1357 return(DestroyImageList(image));
1358 }
1359 (void) jpeg_start_decompress(jpeg_info);
1360 if ((jpeg_info->output_components != 1) &&
1361 (jpeg_info->output_components != 3) && (jpeg_info->output_components != 4))
1362 {
1363 JPEGDestroyDecompress(jpeg_info);
1364 ThrowReaderException(CorruptImageError,"ImageTypeNotSupported");
1365 }
1366 memory_info=AcquireVirtualMemory((size_t) image->columns,
1367 jpeg_info->output_components*sizeof(*jpeg_pixels));
1368 if (memory_info == (MemoryInfo *) NULL)
1369 {
1370 JPEGDestroyDecompress(jpeg_info);
1371 ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed");
1372 }
1373 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
1374 (void) memset(jpeg_pixels,0,image->columns*
1375 jpeg_info->output_components*sizeof(*jpeg_pixels));
1376 /*
1377 Convert JPEG pixels to pixel packets.
1378 */
1379 if (setjmp(error_manager.error_recovery) != 0)
1380 {
1381 if (memory_info != (MemoryInfo *) NULL)
1382 memory_info=RelinquishVirtualMemory(memory_info);
1383 JPEGDestroyDecompress(jpeg_info);
1384 (void) CloseBlob(image);
1385 number_pixels=(MagickSizeType) image->columns*image->rows;
1386 if (number_pixels != 0)
1387 return(GetFirstImageInList(image));
1388 return(DestroyImage(image));
1389 }
1390 if (jpeg_info->quantize_colors != 0)
1391 {
1392 image->colors=(size_t) jpeg_info->actual_number_of_colors;
1393 if (jpeg_info->out_color_space == JCS_GRAYSCALE)
1394 for (i=0; i < (ssize_t) image->colors; i++)
1395 {
1396 image->colormap[i].red=(double) ScaleCharToQuantum(
1397 jpeg_info->colormap[0][i]);
1398 image->colormap[i].green=image->colormap[i].red;
1399 image->colormap[i].blue=image->colormap[i].red;
1400 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
1401 }
1402 else
1403 for (i=0; i < (ssize_t) image->colors; i++)
1404 {
1405 image->colormap[i].red=(double) ScaleCharToQuantum(
1406 jpeg_info->colormap[0][i]);
1407 image->colormap[i].green=(double) ScaleCharToQuantum(
1408 jpeg_info->colormap[1][i]);
1409 image->colormap[i].blue=(double) ScaleCharToQuantum(
1410 jpeg_info->colormap[2][i]);
1411 image->colormap[i].alpha=(MagickRealType) OpaqueAlpha;
1412 }
1413 }
1414 scanline[0]=(JSAMPROW) jpeg_pixels;
1415 for (y=0; y < (ssize_t) image->rows; y++)
1416 {
1417 ssize_t
1418 x;
1419
1420 Quantum
1421 *magick_restrict q;
1422
1423 if (jpeg_read_scanlines(jpeg_info,scanline,1) != 1)
1424 {
1425 (void) ThrowMagickException(exception,GetMagickModule(),
1426 CorruptImageWarning,"SkipToSyncByte","`%s'",image->filename);
1427 continue;
1428 }
1429 p=jpeg_pixels;
1430 q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
1431 if (q == (Quantum *) NULL)
1432 break;
1433 if (jpeg_info->data_precision > 8)
1434 {
1435 unsigned short
1436 scale;
1437
1438 scale=65535/(unsigned short) GetQuantumRange((size_t)
1439 jpeg_info->data_precision);
1440 if (jpeg_info->output_components == 1)
1441 for (x=0; x < (ssize_t) image->columns; x++)
1442 {
1443 ssize_t
1444 pixel;
1445
1446 pixel=(ssize_t) (scale*GETJSAMPLE(*p));
1447 index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
1448 SetPixelIndex(image,index,q);
1449 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
1450 p++;
1451 q+=GetPixelChannels(image);
1452 }
1453 else
1454 if (image->colorspace != CMYKColorspace)
1455 for (x=0; x < (ssize_t) image->columns; x++)
1456 {
1457 SetPixelRed(image,ScaleShortToQuantum(
1458 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1459 SetPixelGreen(image,ScaleShortToQuantum(
1460 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1461 SetPixelBlue(image,ScaleShortToQuantum(
1462 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1463 SetPixelAlpha(image,OpaqueAlpha,q);
1464 q+=GetPixelChannels(image);
1465 }
1466 else
1467 for (x=0; x < (ssize_t) image->columns; x++)
1468 {
1469 SetPixelCyan(image,QuantumRange-ScaleShortToQuantum(
1470 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1471 SetPixelMagenta(image,QuantumRange-ScaleShortToQuantum(
1472 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1473 SetPixelYellow(image,QuantumRange-ScaleShortToQuantum(
1474 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1475 SetPixelBlack(image,QuantumRange-ScaleShortToQuantum(
1476 (unsigned short) (scale*GETJSAMPLE(*p++))),q);
1477 SetPixelAlpha(image,OpaqueAlpha,q);
1478 q+=GetPixelChannels(image);
1479 }
1480 }
1481 else
1482 if (jpeg_info->output_components == 1)
1483 for (x=0; x < (ssize_t) image->columns; x++)
1484 {
1485 ssize_t
1486 pixel;
1487
1488 pixel=(ssize_t) GETJSAMPLE(*p);
1489 index=(Quantum) ConstrainColormapIndex(image,pixel,exception);
1490 SetPixelIndex(image,index,q);
1491 SetPixelViaPixelInfo(image,image->colormap+(ssize_t) index,q);
1492 p++;
1493 q+=GetPixelChannels(image);
1494 }
1495 else
1496 if (image->colorspace != CMYKColorspace)
1497 for (x=0; x < (ssize_t) image->columns; x++)
1498 {
1499 SetPixelRed(image,ScaleCharToQuantum((unsigned char)
1500 GETJSAMPLE(*p++)),q);
1501 SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
1502 GETJSAMPLE(*p++)),q);
1503 SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
1504 GETJSAMPLE(*p++)),q);
1505 SetPixelAlpha(image,OpaqueAlpha,q);
1506 q+=GetPixelChannels(image);
1507 }
1508 else
1509 for (x=0; x < (ssize_t) image->columns; x++)
1510 {
1511 SetPixelCyan(image,QuantumRange-ScaleCharToQuantum(
1512 (unsigned char) GETJSAMPLE(*p++)),q);
1513 SetPixelMagenta(image,QuantumRange-ScaleCharToQuantum(
1514 (unsigned char) GETJSAMPLE(*p++)),q);
1515 SetPixelYellow(image,QuantumRange-ScaleCharToQuantum(
1516 (unsigned char) GETJSAMPLE(*p++)),q);
1517 SetPixelBlack(image,QuantumRange-ScaleCharToQuantum(
1518 (unsigned char) GETJSAMPLE(*p++)),q);
1519 SetPixelAlpha(image,OpaqueAlpha,q);
1520 q+=GetPixelChannels(image);
1521 }
1522 if (SyncAuthenticPixels(image,exception) == MagickFalse)
1523 break;
1524 status=SetImageProgress(image,LoadImageTag,(MagickOffsetType) y,
1525 image->rows);
1526 if (status == MagickFalse)
1527 {
1528 jpeg_abort_decompress(jpeg_info);
1529 break;
1530 }
1531 }
1532 if (status != MagickFalse)
1533 {
1534 error_manager.finished=MagickTrue;
1535 if (setjmp(error_manager.error_recovery) == 0)
1536 (void) jpeg_finish_decompress(jpeg_info);
1537 }
1538 /*
1539 Free jpeg resources.
1540 */
1541 JPEGDestroyDecompress(jpeg_info);
1542 memory_info=RelinquishVirtualMemory(memory_info);
1543 (void) CloseBlob(image);
1544 return(GetFirstImageInList(image));
1545 }
1546
ReadJPEGImage(const ImageInfo * image_info,ExceptionInfo * exception)1547 static Image *ReadJPEGImage(const ImageInfo *image_info,
1548 ExceptionInfo *exception)
1549 {
1550 struct jpeg_decompress_struct
1551 jpeg_info;
1552
1553 return(ReadJPEGImage_(image_info,&jpeg_info,exception));
1554 }
1555 #endif
1556
1557 /*
1558 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1559 % %
1560 % %
1561 % %
1562 % R e g i s t e r J P E G I m a g e %
1563 % %
1564 % %
1565 % %
1566 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1567 %
1568 % RegisterJPEGImage() adds properties for the JPEG image format to
1569 % the list of supported formats. The properties include the image format
1570 % tag, a method to read and/or write the format, whether the format
1571 % supports the saving of more than one frame to the same file or blob,
1572 % whether the format supports native in-memory I/O, and a brief
1573 % description of the format.
1574 %
1575 % The format of the RegisterJPEGImage method is:
1576 %
1577 % size_t RegisterJPEGImage(void)
1578 %
1579 */
RegisterJPEGImage(void)1580 ModuleExport size_t RegisterJPEGImage(void)
1581 {
1582 #define JPEGDescription "Joint Photographic Experts Group JFIF format"
1583 #define JPEGStringify(macro_or_string) JPEGStringifyArg(macro_or_string)
1584 #define JPEGStringifyArg(contents) #contents
1585
1586 char
1587 version[MagickPathExtent];
1588
1589 MagickInfo
1590 *entry;
1591
1592 *version='\0';
1593 #if defined(LIBJPEG_TURBO_VERSION)
1594 (void) CopyMagickString(version,"libjpeg-turbo " JPEGStringify(
1595 LIBJPEG_TURBO_VERSION),MagickPathExtent);
1596 #elif defined(JPEG_LIB_VERSION)
1597 (void) FormatLocaleString(version,MagickPathExtent,"libjpeg %d",
1598 JPEG_LIB_VERSION);
1599 #endif
1600 entry=AcquireMagickInfo("JPEG","JPE",JPEGDescription);
1601 #if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1602 entry->flags^=CoderDecoderThreadSupportFlag;
1603 #endif
1604 #if defined(MAGICKCORE_JPEG_DELEGATE)
1605 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1606 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1607 #endif
1608 entry->magick=(IsImageFormatHandler *) IsJPEG;
1609 entry->flags|=CoderDecoderSeekableStreamFlag;
1610 entry->flags^=CoderAdjoinFlag;
1611 entry->flags^=CoderUseExtensionFlag;
1612 if (*version != '\0')
1613 entry->version=ConstantString(version);
1614 entry->mime_type=ConstantString("image/jpeg");
1615 (void) RegisterMagickInfo(entry);
1616 entry=AcquireMagickInfo("JPEG","JPEG",JPEGDescription);
1617 #if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1618 entry->flags^=CoderDecoderThreadSupportFlag;
1619 #endif
1620 #if defined(MAGICKCORE_JPEG_DELEGATE)
1621 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1622 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1623 #endif
1624 entry->magick=(IsImageFormatHandler *) IsJPEG;
1625 entry->flags|=CoderDecoderSeekableStreamFlag;
1626 entry->flags^=CoderAdjoinFlag;
1627 if (*version != '\0')
1628 entry->version=ConstantString(version);
1629 entry->mime_type=ConstantString("image/jpeg");
1630 (void) RegisterMagickInfo(entry);
1631 entry=AcquireMagickInfo("JPEG","JPG",JPEGDescription);
1632 #if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1633 entry->flags^=CoderDecoderThreadSupportFlag;
1634 #endif
1635 #if defined(MAGICKCORE_JPEG_DELEGATE)
1636 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1637 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1638 #endif
1639 entry->flags|=CoderDecoderSeekableStreamFlag;
1640 entry->flags^=CoderAdjoinFlag;
1641 entry->flags^=CoderUseExtensionFlag;
1642 if (*version != '\0')
1643 entry->version=ConstantString(version);
1644 entry->mime_type=ConstantString("image/jpeg");
1645 (void) RegisterMagickInfo(entry);
1646 entry=AcquireMagickInfo("JPEG","JPS",JPEGDescription);
1647 #if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1648 entry->flags^=CoderDecoderThreadSupportFlag;
1649 #endif
1650 #if defined(MAGICKCORE_JPEG_DELEGATE)
1651 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1652 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1653 #endif
1654 entry->flags|=CoderDecoderSeekableStreamFlag;
1655 entry->flags^=CoderAdjoinFlag;
1656 entry->flags^=CoderUseExtensionFlag;
1657 if (*version != '\0')
1658 entry->version=ConstantString(version);
1659 entry->mime_type=ConstantString("image/jpeg");
1660 (void) RegisterMagickInfo(entry);
1661 entry=AcquireMagickInfo("JPEG","PJPEG",JPEGDescription);
1662 #if (JPEG_LIB_VERSION < 80) && !defined(LIBJPEG_TURBO_VERSION)
1663 entry->flags^=CoderDecoderThreadSupportFlag;
1664 #endif
1665 #if defined(MAGICKCORE_JPEG_DELEGATE)
1666 entry->decoder=(DecodeImageHandler *) ReadJPEGImage;
1667 entry->encoder=(EncodeImageHandler *) WriteJPEGImage;
1668 #endif
1669 entry->flags|=CoderDecoderSeekableStreamFlag;
1670 entry->flags^=CoderAdjoinFlag;
1671 entry->flags^=CoderUseExtensionFlag;
1672 if (*version != '\0')
1673 entry->version=ConstantString(version);
1674 entry->mime_type=ConstantString("image/jpeg");
1675 (void) RegisterMagickInfo(entry);
1676 return(MagickImageCoderSignature);
1677 }
1678
1679 /*
1680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 % %
1682 % %
1683 % %
1684 % U n r e g i s t e r J P E G I m a g e %
1685 % %
1686 % %
1687 % %
1688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1689 %
1690 % UnregisterJPEGImage() removes format registrations made by the
1691 % JPEG module from the list of supported formats.
1692 %
1693 % The format of the UnregisterJPEGImage method is:
1694 %
1695 % UnregisterJPEGImage(void)
1696 %
1697 */
UnregisterJPEGImage(void)1698 ModuleExport void UnregisterJPEGImage(void)
1699 {
1700 (void) UnregisterMagickInfo("PJPG");
1701 (void) UnregisterMagickInfo("JPS");
1702 (void) UnregisterMagickInfo("JPG");
1703 (void) UnregisterMagickInfo("JPEG");
1704 (void) UnregisterMagickInfo("JPE");
1705 }
1706
1707 #if defined(MAGICKCORE_JPEG_DELEGATE)
1708 /*
1709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1710 % %
1711 % %
1712 % %
1713 % W r i t e J P E G I m a g e %
1714 % %
1715 % %
1716 % %
1717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718 %
1719 % WriteJPEGImage() writes a JPEG image file and returns it. It
1720 % allocates the memory necessary for the new Image structure and returns a
1721 % pointer to the new image.
1722 %
1723 % The format of the WriteJPEGImage method is:
1724 %
1725 % MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
1726 % Image *image,ExceptionInfo *exception)
1727 %
1728 % A description of each parameter follows:
1729 %
1730 % o image_info: the image info.
1731 %
1732 % o jpeg_image: The image.
1733 %
1734 % o exception: return any errors or warnings in this structure.
1735 %
1736 */
1737
DestroyQuantizationTable(QuantizationTable * table)1738 static QuantizationTable *DestroyQuantizationTable(QuantizationTable *table)
1739 {
1740 assert(table != (QuantizationTable *) NULL);
1741 if (table->slot != (char *) NULL)
1742 table->slot=DestroyString(table->slot);
1743 if (table->description != (char *) NULL)
1744 table->description=DestroyString(table->description);
1745 if (table->levels != (unsigned int *) NULL)
1746 table->levels=(unsigned int *) RelinquishMagickMemory(table->levels);
1747 table=(QuantizationTable *) RelinquishMagickMemory(table);
1748 return(table);
1749 }
1750
EmptyOutputBuffer(j_compress_ptr cinfo)1751 static boolean EmptyOutputBuffer(j_compress_ptr cinfo)
1752 {
1753 DestinationManager
1754 *destination;
1755
1756 destination=(DestinationManager *) cinfo->dest;
1757 destination->manager.free_in_buffer=(size_t) WriteBlob(destination->image,
1758 MagickMinBufferExtent,destination->buffer);
1759 if (destination->manager.free_in_buffer != MagickMinBufferExtent)
1760 ERREXIT(cinfo,JERR_FILE_WRITE);
1761 destination->manager.next_output_byte=destination->buffer;
1762 return(TRUE);
1763 }
1764
GetQuantizationTable(const char * filename,const char * slot,ExceptionInfo * exception)1765 static QuantizationTable *GetQuantizationTable(const char *filename,
1766 const char *slot,ExceptionInfo *exception)
1767 {
1768 char
1769 *p,
1770 *xml;
1771
1772 const char
1773 *attribute,
1774 *content;
1775
1776 double
1777 value;
1778
1779 ssize_t
1780 i;
1781
1782 ssize_t
1783 j;
1784
1785 QuantizationTable
1786 *table;
1787
1788 size_t
1789 length;
1790
1791 XMLTreeInfo
1792 *description,
1793 *levels,
1794 *quantization_tables,
1795 *table_iterator;
1796
1797 (void) LogMagickEvent(ConfigureEvent,GetMagickModule(),
1798 "Loading quantization tables \"%s\" ...",filename);
1799 table=(QuantizationTable *) NULL;
1800 xml=FileToString(filename,~0UL,exception);
1801 if (xml == (char *) NULL)
1802 return(table);
1803 quantization_tables=NewXMLTree(xml,exception);
1804 if (quantization_tables == (XMLTreeInfo *) NULL)
1805 {
1806 xml=DestroyString(xml);
1807 return(table);
1808 }
1809 for (table_iterator=GetXMLTreeChild(quantization_tables,"table");
1810 table_iterator != (XMLTreeInfo *) NULL;
1811 table_iterator=GetNextXMLTreeTag(table_iterator))
1812 {
1813 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1814 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1815 break;
1816 attribute=GetXMLTreeAttribute(table_iterator,"alias");
1817 if ((attribute != (char *) NULL) && (LocaleCompare(slot,attribute) == 0))
1818 break;
1819 }
1820 if (table_iterator == (XMLTreeInfo *) NULL)
1821 {
1822 xml=DestroyString(xml);
1823 return(table);
1824 }
1825 description=GetXMLTreeChild(table_iterator,"description");
1826 if (description == (XMLTreeInfo *) NULL)
1827 {
1828 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1829 "XmlMissingElement","<description>, slot \"%s\"",slot);
1830 quantization_tables=DestroyXMLTree(quantization_tables);
1831 xml=DestroyString(xml);
1832 return(table);
1833 }
1834 levels=GetXMLTreeChild(table_iterator,"levels");
1835 if (levels == (XMLTreeInfo *) NULL)
1836 {
1837 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1838 "XmlMissingElement","<levels>, slot \"%s\"",slot);
1839 quantization_tables=DestroyXMLTree(quantization_tables);
1840 xml=DestroyString(xml);
1841 return(table);
1842 }
1843 table=(QuantizationTable *) AcquireCriticalMemory(sizeof(*table));
1844 table->slot=(char *) NULL;
1845 table->description=(char *) NULL;
1846 table->levels=(unsigned int *) NULL;
1847 attribute=GetXMLTreeAttribute(table_iterator,"slot");
1848 if (attribute != (char *) NULL)
1849 table->slot=ConstantString(attribute);
1850 content=GetXMLTreeContent(description);
1851 if (content != (char *) NULL)
1852 table->description=ConstantString(content);
1853 attribute=GetXMLTreeAttribute(levels,"width");
1854 if (attribute == (char *) NULL)
1855 {
1856 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1857 "XmlMissingAttribute","<levels width>, slot \"%s\"",slot);
1858 quantization_tables=DestroyXMLTree(quantization_tables);
1859 table=DestroyQuantizationTable(table);
1860 xml=DestroyString(xml);
1861 return(table);
1862 }
1863 table->width=StringToUnsignedLong(attribute);
1864 if (table->width == 0)
1865 {
1866 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1867 "XmlInvalidAttribute","<levels width>, table \"%s\"",slot);
1868 quantization_tables=DestroyXMLTree(quantization_tables);
1869 table=DestroyQuantizationTable(table);
1870 xml=DestroyString(xml);
1871 return(table);
1872 }
1873 attribute=GetXMLTreeAttribute(levels,"height");
1874 if (attribute == (char *) NULL)
1875 {
1876 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1877 "XmlMissingAttribute","<levels height>, table \"%s\"",slot);
1878 quantization_tables=DestroyXMLTree(quantization_tables);
1879 table=DestroyQuantizationTable(table);
1880 xml=DestroyString(xml);
1881 return(table);
1882 }
1883 table->height=StringToUnsignedLong(attribute);
1884 if (table->height == 0)
1885 {
1886 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1887 "XmlInvalidAttribute","<levels height>, table \"%s\"",slot);
1888 quantization_tables=DestroyXMLTree(quantization_tables);
1889 table=DestroyQuantizationTable(table);
1890 xml=DestroyString(xml);
1891 return(table);
1892 }
1893 attribute=GetXMLTreeAttribute(levels,"divisor");
1894 if (attribute == (char *) NULL)
1895 {
1896 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1897 "XmlMissingAttribute","<levels divisor>, table \"%s\"",slot);
1898 quantization_tables=DestroyXMLTree(quantization_tables);
1899 table=DestroyQuantizationTable(table);
1900 xml=DestroyString(xml);
1901 return(table);
1902 }
1903 table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
1904 if (table->divisor == 0.0)
1905 {
1906 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1907 "XmlInvalidAttribute","<levels divisor>, table \"%s\"",slot);
1908 quantization_tables=DestroyXMLTree(quantization_tables);
1909 table=DestroyQuantizationTable(table);
1910 xml=DestroyString(xml);
1911 return(table);
1912 }
1913 content=GetXMLTreeContent(levels);
1914 if (content == (char *) NULL)
1915 {
1916 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1917 "XmlMissingContent","<levels>, table \"%s\"",slot);
1918 quantization_tables=DestroyXMLTree(quantization_tables);
1919 table=DestroyQuantizationTable(table);
1920 xml=DestroyString(xml);
1921 return(table);
1922 }
1923 length=(size_t) table->width*table->height;
1924 if (length < 64)
1925 length=64;
1926 table->levels=(unsigned int *) AcquireQuantumMemory(length,
1927 sizeof(*table->levels));
1928 if (table->levels == (unsigned int *) NULL)
1929 ThrowFatalException(ResourceLimitFatalError,
1930 "UnableToAcquireQuantizationTable");
1931 for (i=0; i < (ssize_t) (table->width*table->height); i++)
1932 {
1933 table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
1934 table->divisor+0.5);
1935 while (isspace((int) ((unsigned char) *p)) != 0)
1936 p++;
1937 if (*p == ',')
1938 p++;
1939 content=p;
1940 }
1941 value=InterpretLocaleValue(content,&p);
1942 (void) value;
1943 if (p != content)
1944 {
1945 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1946 "XmlInvalidContent","<level> too many values, table \"%s\"",slot);
1947 quantization_tables=DestroyXMLTree(quantization_tables);
1948 table=DestroyQuantizationTable(table);
1949 xml=DestroyString(xml);
1950 return(table);
1951 }
1952 for (j=i; j < 64; j++)
1953 table->levels[j]=table->levels[j-1];
1954 quantization_tables=DestroyXMLTree(quantization_tables);
1955 xml=DestroyString(xml);
1956 return(table);
1957 }
1958
InitializeDestination(j_compress_ptr cinfo)1959 static void InitializeDestination(j_compress_ptr cinfo)
1960 {
1961 DestinationManager
1962 *destination;
1963
1964 destination=(DestinationManager *) cinfo->dest;
1965 destination->buffer=(JOCTET *) (*cinfo->mem->alloc_small)
1966 ((j_common_ptr) cinfo,JPOOL_IMAGE,MagickMinBufferExtent*sizeof(JOCTET));
1967 destination->manager.next_output_byte=destination->buffer;
1968 destination->manager.free_in_buffer=MagickMinBufferExtent;
1969 }
1970
TerminateDestination(j_compress_ptr cinfo)1971 static void TerminateDestination(j_compress_ptr cinfo)
1972 {
1973 DestinationManager
1974 *destination;
1975
1976 destination=(DestinationManager *) cinfo->dest;
1977 if ((MagickMinBufferExtent-(int) destination->manager.free_in_buffer) > 0)
1978 {
1979 ssize_t
1980 count;
1981
1982 count=WriteBlob(destination->image,MagickMinBufferExtent-
1983 destination->manager.free_in_buffer,destination->buffer);
1984 if (count != (ssize_t)
1985 (MagickMinBufferExtent-destination->manager.free_in_buffer))
1986 ERREXIT(cinfo,JERR_FILE_WRITE);
1987 }
1988 }
1989
WriteProfile(j_compress_ptr jpeg_info,Image * image,ExceptionInfo * exception)1990 static void WriteProfile(j_compress_ptr jpeg_info,Image *image,
1991 ExceptionInfo *exception)
1992 {
1993 const char
1994 *name;
1995
1996 const StringInfo
1997 *profile;
1998
1999 MagickBooleanType
2000 iptc;
2001
2002 ssize_t
2003 i;
2004
2005 size_t
2006 length,
2007 tag_length;
2008
2009 StringInfo
2010 *custom_profile;
2011
2012 /*
2013 Save image profile as a APP marker.
2014 */
2015 iptc=MagickFalse;
2016 custom_profile=AcquireStringInfo(65535L);
2017 ResetImageProfileIterator(image);
2018 for (name=GetNextImageProfile(image); name != (const char *) NULL; )
2019 {
2020 profile=GetImageProfile(image,name);
2021 length=GetStringInfoLength(profile);
2022 if (LocaleNCompare(name,"APP",3) == 0)
2023 {
2024 int
2025 id;
2026
2027 id=JPEG_APP0+StringToInteger(name+3);
2028 for (i=0; i < (ssize_t) length; i+=65533L)
2029 jpeg_write_marker(jpeg_info,id,GetStringInfoDatum(profile)+i,
2030 (unsigned int) MagickMin(length-i,65533));
2031 }
2032 if (LocaleCompare(name,"EXIF") == 0)
2033 {
2034 length=GetStringInfoLength(profile);
2035 if (length > 65533L)
2036 {
2037 (void) ThrowMagickException(exception,GetMagickModule(),
2038 CoderWarning,"ExifProfileSizeExceedsLimit","`%s'",
2039 image->filename);
2040 length=65533L;
2041 }
2042 jpeg_write_marker(jpeg_info,XML_MARKER,GetStringInfoDatum(profile),
2043 (unsigned int) length);
2044 }
2045 if (LocaleCompare(name,"ICC") == 0)
2046 {
2047 unsigned char
2048 *p;
2049
2050 tag_length=strlen(ICC_PROFILE);
2051 p=GetStringInfoDatum(custom_profile);
2052 (void) memcpy(p,ICC_PROFILE,tag_length);
2053 p[tag_length]='\0';
2054 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65519L)
2055 {
2056 length=MagickMin(GetStringInfoLength(profile)-i,65519L);
2057 p[12]=(unsigned char) ((i/65519L)+1);
2058 p[13]=(unsigned char) (GetStringInfoLength(profile)/65519L+1);
2059 (void) memcpy(p+tag_length+3,GetStringInfoDatum(profile)+i,
2060 length);
2061 jpeg_write_marker(jpeg_info,ICC_MARKER,GetStringInfoDatum(
2062 custom_profile),(unsigned int) (length+tag_length+3));
2063 }
2064 }
2065 if (((LocaleCompare(name,"IPTC") == 0) ||
2066 (LocaleCompare(name,"8BIM") == 0)) && (iptc == MagickFalse))
2067 {
2068 unsigned char
2069 *p;
2070
2071 size_t
2072 roundup;
2073
2074 iptc=MagickTrue;
2075 p=GetStringInfoDatum(custom_profile);
2076 for (i=0; i < (ssize_t) GetStringInfoLength(profile); i+=65500L)
2077 {
2078 length=MagickMin(GetStringInfoLength(profile)-i,65500L);
2079 roundup=(size_t) (length & 0x01);
2080 if (LocaleNCompare((char *) GetStringInfoDatum(profile),"8BIM",4) == 0)
2081 {
2082 (void) memcpy(p,"Photoshop 3.0 ",14);
2083 tag_length=14;
2084 }
2085 else
2086 {
2087 (void) memcpy(p,"Photoshop 3.0 8BIM\04\04\0\0\0\0",24);
2088 tag_length=26;
2089 p[24]=(unsigned char) (length >> 8);
2090 p[25]=(unsigned char) (length & 0xff);
2091 }
2092 p[13]=0x00;
2093 (void) memcpy(p+tag_length,GetStringInfoDatum(profile)+i,length);
2094 if (roundup != 0)
2095 p[length+tag_length]='\0';
2096 jpeg_write_marker(jpeg_info,IPTC_MARKER,GetStringInfoDatum(
2097 custom_profile),(unsigned int) (length+tag_length+roundup));
2098 }
2099 }
2100 if ((LocaleCompare(name,"XMP") == 0) &&
2101 (GetStringInfoLength(profile) < (65533 - sizeof(xmp_namespace))))
2102 {
2103 StringInfo
2104 *xmp_profile;
2105
2106 /*
2107 Add namespace to XMP profile.
2108 */
2109 xmp_profile=StringToStringInfo(xmp_namespace);
2110 if (xmp_profile != (StringInfo *) NULL)
2111 {
2112 ConcatenateStringInfo(xmp_profile,profile);
2113 GetStringInfoDatum(xmp_profile)[XmpNamespaceExtent]='\0';
2114 length=GetStringInfoLength(xmp_profile);
2115 jpeg_write_marker(jpeg_info,XML_MARKER,
2116 GetStringInfoDatum(xmp_profile),(unsigned int) length);
2117 xmp_profile=DestroyStringInfo(xmp_profile);
2118 }
2119 }
2120 if (image->debug != MagickFalse)
2121 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2122 "%s profile: %.20g bytes",name,(double) GetStringInfoLength(profile));
2123 name=GetNextImageProfile(image);
2124 }
2125 custom_profile=DestroyStringInfo(custom_profile);
2126 }
2127
JPEGDestinationManager(j_compress_ptr cinfo,Image * image)2128 static void JPEGDestinationManager(j_compress_ptr cinfo,Image * image)
2129 {
2130 DestinationManager
2131 *destination;
2132
2133 cinfo->dest=(struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small)
2134 ((j_common_ptr) cinfo,JPOOL_IMAGE,sizeof(DestinationManager));
2135 destination=(DestinationManager *) cinfo->dest;
2136 destination->manager.init_destination=InitializeDestination;
2137 destination->manager.empty_output_buffer=EmptyOutputBuffer;
2138 destination->manager.term_destination=TerminateDestination;
2139 destination->image=image;
2140 }
2141
SamplingFactorToList(const char * text)2142 static char **SamplingFactorToList(const char *text)
2143 {
2144 char
2145 **textlist;
2146
2147 char
2148 *q;
2149
2150 const char
2151 *p;
2152
2153 ssize_t
2154 i;
2155
2156 if (text == (char *) NULL)
2157 return((char **) NULL);
2158 /*
2159 Convert string to an ASCII list.
2160 */
2161 textlist=(char **) AcquireQuantumMemory((size_t) MAX_COMPONENTS,
2162 sizeof(*textlist));
2163 if (textlist == (char **) NULL)
2164 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2165 p=text;
2166 for (i=0; i < (ssize_t) MAX_COMPONENTS; i++)
2167 {
2168 for (q=(char *) p; *q != '\0'; q++)
2169 if (*q == ',')
2170 break;
2171 textlist[i]=(char *) AcquireQuantumMemory((size_t) (q-p)+MagickPathExtent,
2172 sizeof(*textlist[i]));
2173 if (textlist[i] == (char *) NULL)
2174 ThrowFatalException(ResourceLimitFatalError,"UnableToConvertText");
2175 (void) CopyMagickString(textlist[i],p,(size_t) (q-p+1));
2176 if (*q == '\r')
2177 q++;
2178 if (*q == '\0')
2179 break;
2180 p=q+1;
2181 }
2182 for (i++; i < (ssize_t) MAX_COMPONENTS; i++)
2183 textlist[i]=ConstantString("1x1");
2184 return(textlist);
2185 }
2186
WriteJPEGImage_(const ImageInfo * image_info,Image * image,struct jpeg_compress_struct * jpeg_info,ExceptionInfo * exception)2187 static MagickBooleanType WriteJPEGImage_(const ImageInfo *image_info,
2188 Image *image,struct jpeg_compress_struct *jpeg_info,ExceptionInfo *exception)
2189 {
2190 const char
2191 *dct_method,
2192 *option,
2193 *sampling_factor,
2194 *value;
2195
2196 ErrorManager
2197 error_manager;
2198
2199 Image
2200 *volatile volatile_image;
2201
2202 int
2203 colorspace,
2204 quality;
2205
2206 JSAMPLE
2207 *volatile jpeg_pixels;
2208
2209 JSAMPROW
2210 scanline[1];
2211
2212 MagickBooleanType
2213 status;
2214
2215 MemoryInfo
2216 *memory_info;
2217
2218 JSAMPLE
2219 *q;
2220
2221 ssize_t
2222 i;
2223
2224 ssize_t
2225 y;
2226
2227 struct jpeg_error_mgr
2228 jpeg_error;
2229
2230 unsigned short
2231 scale;
2232
2233 /*
2234 Open image file.
2235 */
2236 assert(image_info != (const ImageInfo *) NULL);
2237 assert(image_info->signature == MagickCoreSignature);
2238 assert(image != (Image *) NULL);
2239 assert(image->signature == MagickCoreSignature);
2240 if (image->debug != MagickFalse)
2241 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2242 assert(exception != (ExceptionInfo *) NULL);
2243 assert(exception->signature == MagickCoreSignature);
2244 if ((LocaleCompare(image_info->magick,"JPS") == 0) &&
2245 (image->next != (Image *) NULL))
2246 image=AppendImages(image,MagickFalse,exception);
2247 status=OpenBlob(image_info,image,WriteBinaryBlobMode,exception);
2248 if (status == MagickFalse)
2249 return(status);
2250 /*
2251 Initialize JPEG parameters.
2252 */
2253 (void) memset(&error_manager,0,sizeof(error_manager));
2254 (void) memset(jpeg_info,0,sizeof(*jpeg_info));
2255 (void) memset(&jpeg_error,0,sizeof(jpeg_error));
2256 volatile_image=image;
2257 jpeg_info->client_data=(void *) volatile_image;
2258 jpeg_info->err=jpeg_std_error(&jpeg_error);
2259 jpeg_info->err->emit_message=(void (*)(j_common_ptr,int)) JPEGWarningHandler;
2260 jpeg_info->err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
2261 error_manager.exception=exception;
2262 error_manager.image=volatile_image;
2263 memory_info=(MemoryInfo *) NULL;
2264 if (setjmp(error_manager.error_recovery) != 0)
2265 {
2266 jpeg_destroy_compress(jpeg_info);
2267 (void) CloseBlob(volatile_image);
2268 return(MagickFalse);
2269 }
2270 jpeg_info->client_data=(void *) &error_manager;
2271 jpeg_create_compress(jpeg_info);
2272 JPEGDestinationManager(jpeg_info,image);
2273 if ((image->columns != (unsigned int) image->columns) ||
2274 (image->rows != (unsigned int) image->rows))
2275 ThrowWriterException(ImageError,"WidthOrHeightExceedsLimit");
2276 jpeg_info->image_width=(unsigned int) image->columns;
2277 jpeg_info->image_height=(unsigned int) image->rows;
2278 jpeg_info->input_components=3;
2279 jpeg_info->data_precision=8;
2280 jpeg_info->in_color_space=JCS_RGB;
2281 switch (image->colorspace)
2282 {
2283 case CMYKColorspace:
2284 {
2285 jpeg_info->input_components=4;
2286 jpeg_info->in_color_space=JCS_CMYK;
2287 break;
2288 }
2289 case YCbCrColorspace:
2290 case Rec601YCbCrColorspace:
2291 case Rec709YCbCrColorspace:
2292 {
2293 jpeg_info->in_color_space=JCS_YCbCr;
2294 break;
2295 }
2296 case LinearGRAYColorspace:
2297 case GRAYColorspace:
2298 {
2299 if (image_info->type == TrueColorType)
2300 break;
2301 jpeg_info->input_components=1;
2302 jpeg_info->in_color_space=JCS_GRAYSCALE;
2303 break;
2304 }
2305 default:
2306 {
2307 (void) TransformImageColorspace(image,sRGBColorspace,exception);
2308 if (image_info->type == TrueColorType)
2309 break;
2310 if (SetImageGray(image,exception) != MagickFalse)
2311 {
2312 jpeg_info->input_components=1;
2313 jpeg_info->in_color_space=JCS_GRAYSCALE;
2314 }
2315 break;
2316 }
2317 }
2318 jpeg_set_defaults(jpeg_info);
2319 if (jpeg_info->in_color_space == JCS_CMYK)
2320 jpeg_set_colorspace(jpeg_info,JCS_YCCK);
2321 if ((jpeg_info->data_precision != 12) && (image->depth <= 8))
2322 jpeg_info->data_precision=8;
2323 else
2324 jpeg_info->data_precision=BITS_IN_JSAMPLE;
2325 if (image->debug != MagickFalse)
2326 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2327 "Image resolution: %.20g,%.20g",image->resolution.x,image->resolution.y);
2328 if ((image->resolution.x != 0.0) && (image->resolution.y != 0.0))
2329 {
2330 /*
2331 Set image resolution.
2332 */
2333 jpeg_info->write_JFIF_header=TRUE;
2334 jpeg_info->X_density=(UINT16) image->resolution.x;
2335 jpeg_info->Y_density=(UINT16) image->resolution.y;
2336 /*
2337 Set image resolution units.
2338 */
2339 if (image->units == PixelsPerInchResolution)
2340 jpeg_info->density_unit=(UINT8) 1;
2341 if (image->units == PixelsPerCentimeterResolution)
2342 jpeg_info->density_unit=(UINT8) 2;
2343 }
2344 dct_method=GetImageOption(image_info,"jpeg:dct-method");
2345 if (dct_method != (const char *) NULL)
2346 switch (*dct_method)
2347 {
2348 case 'D':
2349 case 'd':
2350 {
2351 if (LocaleCompare(dct_method,"default") == 0)
2352 jpeg_info->dct_method=JDCT_DEFAULT;
2353 break;
2354 }
2355 case 'F':
2356 case 'f':
2357 {
2358 if (LocaleCompare(dct_method,"fastest") == 0)
2359 jpeg_info->dct_method=JDCT_FASTEST;
2360 if (LocaleCompare(dct_method,"float") == 0)
2361 jpeg_info->dct_method=JDCT_FLOAT;
2362 break;
2363 }
2364 case 'I':
2365 case 'i':
2366 {
2367 if (LocaleCompare(dct_method,"ifast") == 0)
2368 jpeg_info->dct_method=JDCT_IFAST;
2369 if (LocaleCompare(dct_method,"islow") == 0)
2370 jpeg_info->dct_method=JDCT_ISLOW;
2371 break;
2372 }
2373 }
2374 option=GetImageOption(image_info,"jpeg:optimize-coding");
2375 if (option != (const char *) NULL)
2376 jpeg_info->optimize_coding=IsStringTrue(option) != MagickFalse ? TRUE :
2377 FALSE;
2378 else
2379 {
2380 MagickSizeType
2381 length;
2382
2383 length=(MagickSizeType) jpeg_info->input_components*image->columns*
2384 image->rows*sizeof(JSAMPLE);
2385 if (length == (MagickSizeType) ((size_t) length))
2386 {
2387 /*
2388 Perform optimization only if available memory resources permit it.
2389 */
2390 status=AcquireMagickResource(MemoryResource,length);
2391 if (status != MagickFalse)
2392 RelinquishMagickResource(MemoryResource,length);
2393 jpeg_info->optimize_coding=status == MagickFalse ? FALSE : TRUE;
2394 }
2395 }
2396 #if defined(C_ARITH_CODING_SUPPORTED)
2397 option=GetImageOption(image_info,"jpeg:arithmetic-coding");
2398 if (IsStringTrue(option) != MagickFalse)
2399 {
2400 jpeg_info->arith_code=TRUE;
2401 jpeg_info->optimize_coding=FALSE; /* not supported */
2402 }
2403 #endif
2404 #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
2405 if ((LocaleCompare(image_info->magick,"PJPEG") == 0) ||
2406 (image_info->interlace != NoInterlace))
2407 {
2408 if (image->debug != MagickFalse)
2409 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2410 "Interlace: progressive");
2411 jpeg_simple_progression(jpeg_info);
2412 }
2413 else
2414 if (image->debug != MagickFalse)
2415 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2416 "Interlace: non-progressive");
2417 #else
2418 if (image->debug != MagickFalse)
2419 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2420 "Interlace: nonprogressive");
2421 #endif
2422 quality=92;
2423 if ((image_info->compression != LosslessJPEGCompression) &&
2424 (image->quality <= 100))
2425 {
2426 if (image->quality != UndefinedCompressionQuality)
2427 quality=(int) image->quality;
2428 if (image->debug != MagickFalse)
2429 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: %.20g",
2430 (double) image->quality);
2431 }
2432 else
2433 {
2434 #if !defined(C_LOSSLESS_SUPPORTED)
2435 quality=100;
2436 if (image->debug != MagickFalse)
2437 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Quality: 100");
2438 #else
2439 if (image->quality < 100)
2440 (void) ThrowMagickException(exception,GetMagickModule(),CoderWarning,
2441 "LosslessToLossyJPEGConversion","`%s'",image->filename);
2442 else
2443 {
2444 int
2445 point_transform,
2446 predictor;
2447
2448 predictor=image->quality/100; /* range 1-7 */
2449 point_transform=image->quality % 20; /* range 0-15 */
2450 jpeg_simple_lossless(jpeg_info,predictor,point_transform);
2451 if (image->debug != MagickFalse)
2452 {
2453 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2454 "Compression: lossless");
2455 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2456 "Predictor: %d",predictor);
2457 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2458 "Point Transform: %d",point_transform);
2459 }
2460 }
2461 #endif
2462 }
2463 option=GetImageOption(image_info,"jpeg:extent");
2464 if (option != (const char *) NULL)
2465 {
2466 Image
2467 *jpeg_image;
2468
2469 ImageInfo
2470 *extent_info;
2471
2472 extent_info=CloneImageInfo(image_info);
2473 extent_info->blob=NULL;
2474 jpeg_image=CloneImage(image,0,0,MagickTrue,exception);
2475 if (jpeg_image != (Image *) NULL)
2476 {
2477 MagickSizeType
2478 extent;
2479
2480 size_t
2481 maximum,
2482 minimum;
2483
2484 /*
2485 Search for compression quality that does not exceed image extent.
2486 */
2487 extent_info->quality=0;
2488 extent=(MagickSizeType) SiPrefixToDoubleInterval(option,100.0);
2489 (void) DeleteImageOption(extent_info,"jpeg:extent");
2490 (void) DeleteImageArtifact(jpeg_image,"jpeg:extent");
2491 maximum=image_info->quality;
2492 if (maximum < 2)
2493 maximum=101;
2494 for (minimum=2; minimum < maximum; )
2495 {
2496 (void) AcquireUniqueFilename(jpeg_image->filename);
2497 jpeg_image->quality=minimum+(maximum-minimum+1)/2;
2498 status=WriteJPEGImage(extent_info,jpeg_image,exception);
2499 if (GetBlobSize(jpeg_image) <= extent)
2500 minimum=jpeg_image->quality+1;
2501 else
2502 maximum=jpeg_image->quality-1;
2503 (void) RelinquishUniqueFileResource(jpeg_image->filename);
2504 }
2505 quality=(int) minimum-1;
2506 jpeg_image=DestroyImage(jpeg_image);
2507 }
2508 extent_info=DestroyImageInfo(extent_info);
2509 }
2510 jpeg_set_quality(jpeg_info,quality,TRUE);
2511 if ((dct_method == (const char *) NULL) && (quality <= 90))
2512 jpeg_info->dct_method=JDCT_IFAST;
2513 #if (JPEG_LIB_VERSION >= 70)
2514 option=GetImageOption(image_info,"quality");
2515 if (option != (const char *) NULL)
2516 {
2517 GeometryInfo
2518 geometry_info;
2519
2520 int
2521 flags;
2522
2523 /*
2524 Set quality scaling for luminance and chrominance separately.
2525 */
2526 flags=ParseGeometry(option,&geometry_info);
2527 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
2528 {
2529 jpeg_info->q_scale_factor[0]=jpeg_quality_scaling((int)
2530 (geometry_info.rho+0.5));
2531 jpeg_info->q_scale_factor[1]=jpeg_quality_scaling((int)
2532 (geometry_info.sigma+0.5));
2533 jpeg_default_qtables(jpeg_info,TRUE);
2534 }
2535 }
2536 #endif
2537 colorspace=jpeg_info->in_color_space;
2538 value=GetImageOption(image_info,"jpeg:colorspace");
2539 if (value == (char *) NULL)
2540 value=GetImageProperty(image,"jpeg:colorspace",exception);
2541 if (value != (char *) NULL)
2542 colorspace=StringToInteger(value);
2543 sampling_factor=(const char *) NULL;
2544 if ((J_COLOR_SPACE) colorspace == jpeg_info->in_color_space)
2545 {
2546 value=GetImageOption(image_info,"jpeg:sampling-factor");
2547 if (value == (char *) NULL)
2548 value=GetImageProperty(image,"jpeg:sampling-factor",exception);
2549 if (value != (char *) NULL)
2550 {
2551 sampling_factor=value;
2552 if (image->debug != MagickFalse)
2553 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2554 " Input sampling-factors=%s",sampling_factor);
2555 }
2556 }
2557 value=GetImageOption(image_info,"jpeg:sampling-factor");
2558 if (image_info->sampling_factor != (char *) NULL)
2559 sampling_factor=image_info->sampling_factor;
2560 if (sampling_factor == (const char *) NULL)
2561 {
2562 if (quality >= 90)
2563 for (i=0; i < MAX_COMPONENTS; i++)
2564 {
2565 jpeg_info->comp_info[i].h_samp_factor=1;
2566 jpeg_info->comp_info[i].v_samp_factor=1;
2567 }
2568 }
2569 else
2570 {
2571 char
2572 **factors;
2573
2574 GeometryInfo
2575 geometry_info;
2576
2577 MagickStatusType
2578 flags;
2579
2580 /*
2581 Set sampling factor.
2582 */
2583 i=0;
2584 factors=SamplingFactorToList(sampling_factor);
2585 if (factors != (char **) NULL)
2586 {
2587 for (i=0; i < MAX_COMPONENTS; i++)
2588 {
2589 if (factors[i] == (char *) NULL)
2590 break;
2591 flags=ParseGeometry(factors[i],&geometry_info);
2592 if ((flags & SigmaValue) == 0)
2593 geometry_info.sigma=geometry_info.rho;
2594 jpeg_info->comp_info[i].h_samp_factor=(int) geometry_info.rho;
2595 jpeg_info->comp_info[i].v_samp_factor=(int) geometry_info.sigma;
2596 factors[i]=(char *) RelinquishMagickMemory(factors[i]);
2597 }
2598 factors=(char **) RelinquishMagickMemory(factors);
2599 }
2600 for ( ; i < MAX_COMPONENTS; i++)
2601 {
2602 jpeg_info->comp_info[i].h_samp_factor=1;
2603 jpeg_info->comp_info[i].v_samp_factor=1;
2604 }
2605 }
2606 option=GetImageOption(image_info,"jpeg:q-table");
2607 if (option != (const char *) NULL)
2608 {
2609 QuantizationTable
2610 *table;
2611
2612 /*
2613 Custom quantization tables.
2614 */
2615 table=GetQuantizationTable(option,"0",exception);
2616 if (table != (QuantizationTable *) NULL)
2617 {
2618 for (i=0; i < MAX_COMPONENTS; i++)
2619 jpeg_info->comp_info[i].quant_tbl_no=0;
2620 jpeg_add_quant_table(jpeg_info,0,table->levels,
2621 jpeg_quality_scaling(quality),0);
2622 table=DestroyQuantizationTable(table);
2623 }
2624 table=GetQuantizationTable(option,"1",exception);
2625 if (table != (QuantizationTable *) NULL)
2626 {
2627 for (i=1; i < MAX_COMPONENTS; i++)
2628 jpeg_info->comp_info[i].quant_tbl_no=1;
2629 jpeg_add_quant_table(jpeg_info,1,table->levels,
2630 jpeg_quality_scaling(quality),0);
2631 table=DestroyQuantizationTable(table);
2632 }
2633 table=GetQuantizationTable(option,"2",exception);
2634 if (table != (QuantizationTable *) NULL)
2635 {
2636 for (i=2; i < MAX_COMPONENTS; i++)
2637 jpeg_info->comp_info[i].quant_tbl_no=2;
2638 jpeg_add_quant_table(jpeg_info,2,table->levels,
2639 jpeg_quality_scaling(quality),0);
2640 table=DestroyQuantizationTable(table);
2641 }
2642 table=GetQuantizationTable(option,"3",exception);
2643 if (table != (QuantizationTable *) NULL)
2644 {
2645 for (i=3; i < MAX_COMPONENTS; i++)
2646 jpeg_info->comp_info[i].quant_tbl_no=3;
2647 jpeg_add_quant_table(jpeg_info,3,table->levels,
2648 jpeg_quality_scaling(quality),0);
2649 table=DestroyQuantizationTable(table);
2650 }
2651 }
2652 jpeg_start_compress(jpeg_info,TRUE);
2653 if (image->debug != MagickFalse)
2654 {
2655 if (image->storage_class == PseudoClass)
2656 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2657 "Storage class: PseudoClass");
2658 else
2659 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2660 "Storage class: DirectClass");
2661 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Depth: %.20g",
2662 (double) image->depth);
2663 if (image->colors != 0)
2664 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2665 "Number of colors: %.20g",(double) image->colors);
2666 else
2667 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2668 "Number of colors: unspecified");
2669 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2670 "JPEG data precision: %d",(int) jpeg_info->data_precision);
2671 switch (image->colorspace)
2672 {
2673 case CMYKColorspace:
2674 {
2675 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2676 "Storage class: DirectClass");
2677 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2678 "Colorspace: CMYK");
2679 break;
2680 }
2681 case YCbCrColorspace:
2682 case Rec601YCbCrColorspace:
2683 case Rec709YCbCrColorspace:
2684 {
2685 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2686 "Colorspace: YCbCr");
2687 break;
2688 }
2689 default:
2690 break;
2691 }
2692 switch (image->colorspace)
2693 {
2694 case CMYKColorspace:
2695 {
2696 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2697 "Colorspace: CMYK");
2698 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2699 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2700 jpeg_info->comp_info[0].h_samp_factor,
2701 jpeg_info->comp_info[0].v_samp_factor,
2702 jpeg_info->comp_info[1].h_samp_factor,
2703 jpeg_info->comp_info[1].v_samp_factor,
2704 jpeg_info->comp_info[2].h_samp_factor,
2705 jpeg_info->comp_info[2].v_samp_factor,
2706 jpeg_info->comp_info[3].h_samp_factor,
2707 jpeg_info->comp_info[3].v_samp_factor);
2708 break;
2709 }
2710 case GRAYColorspace:
2711 {
2712 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2713 "Colorspace: GRAY");
2714 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2715 "Sampling factors: %dx%d",jpeg_info->comp_info[0].h_samp_factor,
2716 jpeg_info->comp_info[0].v_samp_factor);
2717 break;
2718 }
2719 case sRGBColorspace:
2720 case RGBColorspace:
2721 {
2722 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2723 "Image colorspace is RGB");
2724 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2725 "Sampling factors: %dx%d,%dx%d,%dx%d",
2726 jpeg_info->comp_info[0].h_samp_factor,
2727 jpeg_info->comp_info[0].v_samp_factor,
2728 jpeg_info->comp_info[1].h_samp_factor,
2729 jpeg_info->comp_info[1].v_samp_factor,
2730 jpeg_info->comp_info[2].h_samp_factor,
2731 jpeg_info->comp_info[2].v_samp_factor);
2732 break;
2733 }
2734 case YCbCrColorspace:
2735 case Rec601YCbCrColorspace:
2736 case Rec709YCbCrColorspace:
2737 {
2738 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2739 "Colorspace: YCbCr");
2740 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2741 "Sampling factors: %dx%d,%dx%d,%dx%d",
2742 jpeg_info->comp_info[0].h_samp_factor,
2743 jpeg_info->comp_info[0].v_samp_factor,
2744 jpeg_info->comp_info[1].h_samp_factor,
2745 jpeg_info->comp_info[1].v_samp_factor,
2746 jpeg_info->comp_info[2].h_samp_factor,
2747 jpeg_info->comp_info[2].v_samp_factor);
2748 break;
2749 }
2750 default:
2751 {
2752 (void) LogMagickEvent(CoderEvent,GetMagickModule(),"Colorspace: %d",
2753 image->colorspace);
2754 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
2755 "Sampling factors: %dx%d,%dx%d,%dx%d,%dx%d",
2756 jpeg_info->comp_info[0].h_samp_factor,
2757 jpeg_info->comp_info[0].v_samp_factor,
2758 jpeg_info->comp_info[1].h_samp_factor,
2759 jpeg_info->comp_info[1].v_samp_factor,
2760 jpeg_info->comp_info[2].h_samp_factor,
2761 jpeg_info->comp_info[2].v_samp_factor,
2762 jpeg_info->comp_info[3].h_samp_factor,
2763 jpeg_info->comp_info[3].v_samp_factor);
2764 break;
2765 }
2766 }
2767 }
2768 /*
2769 Write JPEG profiles.
2770 */
2771 value=GetImageProperty(image,"comment",exception);
2772 if (value != (char *) NULL)
2773 {
2774 size_t
2775 length;
2776
2777 length=strlen(value);
2778 for (i=0; i < (ssize_t) length; i+=65533L)
2779 jpeg_write_marker(jpeg_info,JPEG_COM,(unsigned char *) value+i,
2780 (unsigned int) MagickMin((size_t) strlen(value+i),65533L));
2781 }
2782 if (image->profiles != (void *) NULL)
2783 WriteProfile(jpeg_info,image,exception);
2784 /*
2785 Convert MIFF to JPEG raster pixels.
2786 */
2787 memory_info=AcquireVirtualMemory((size_t) image->columns,
2788 jpeg_info->input_components*sizeof(*jpeg_pixels));
2789 if (memory_info == (MemoryInfo *) NULL)
2790 ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
2791 jpeg_pixels=(JSAMPLE *) GetVirtualMemoryBlob(memory_info);
2792 if (setjmp(error_manager.error_recovery) != 0)
2793 {
2794 jpeg_destroy_compress(jpeg_info);
2795 if (memory_info != (MemoryInfo *) NULL)
2796 memory_info=RelinquishVirtualMemory(memory_info);
2797 (void) CloseBlob(image);
2798 return(MagickFalse);
2799 }
2800 scanline[0]=(JSAMPROW) jpeg_pixels;
2801 scale=65535/(unsigned short) GetQuantumRange((size_t)
2802 jpeg_info->data_precision);
2803 if (scale == 0)
2804 scale=1;
2805 if (jpeg_info->data_precision <= 8)
2806 {
2807 if ((jpeg_info->in_color_space == JCS_RGB) ||
2808 (jpeg_info->in_color_space == JCS_YCbCr))
2809 for (y=0; y < (ssize_t) image->rows; y++)
2810 {
2811 const Quantum
2812 *p;
2813
2814 ssize_t
2815 x;
2816
2817 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2818 if (p == (const Quantum *) NULL)
2819 break;
2820 q=jpeg_pixels;
2821 for (x=0; x < (ssize_t) image->columns; x++)
2822 {
2823 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelRed(image,p));
2824 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelGreen(image,p));
2825 *q++=(JSAMPLE) ScaleQuantumToChar(GetPixelBlue(image,p));
2826 p+=GetPixelChannels(image);
2827 }
2828 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2829 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2830 image->rows);
2831 if (status == MagickFalse)
2832 break;
2833 }
2834 else
2835 if (jpeg_info->in_color_space == JCS_GRAYSCALE)
2836 for (y=0; y < (ssize_t) image->rows; y++)
2837 {
2838 const Quantum
2839 *p;
2840
2841 ssize_t
2842 x;
2843
2844 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2845 if (p == (const Quantum *) NULL)
2846 break;
2847 q=jpeg_pixels;
2848 for (x=0; x < (ssize_t) image->columns; x++)
2849 {
2850 *q++=(JSAMPLE) ScaleQuantumToChar(ClampToQuantum(GetPixelLuma(
2851 image,p)));
2852 p+=GetPixelChannels(image);
2853 }
2854 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2855 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2856 image->rows);
2857 if (status == MagickFalse)
2858 break;
2859 }
2860 else
2861 for (y=0; y < (ssize_t) image->rows; y++)
2862 {
2863 const Quantum
2864 *p;
2865
2866 ssize_t
2867 x;
2868
2869 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2870 if (p == (const Quantum *) NULL)
2871 break;
2872 q=jpeg_pixels;
2873 for (x=0; x < (ssize_t) image->columns; x++)
2874 {
2875 /*
2876 Convert DirectClass packets to contiguous CMYK scanlines.
2877 */
2878 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2879 GetPixelCyan(image,p))));
2880 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2881 GetPixelMagenta(image,p))));
2882 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2883 GetPixelYellow(image,p))));
2884 *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
2885 GetPixelBlack(image,p))));
2886 p+=GetPixelChannels(image);
2887 }
2888 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2889 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2890 image->rows);
2891 if (status == MagickFalse)
2892 break;
2893 }
2894 }
2895 else
2896 if (jpeg_info->in_color_space == JCS_GRAYSCALE)
2897 for (y=0; y < (ssize_t) image->rows; y++)
2898 {
2899 const Quantum
2900 *p;
2901
2902 ssize_t
2903 x;
2904
2905 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2906 if (p == (const Quantum *) NULL)
2907 break;
2908 q=jpeg_pixels;
2909 for (x=0; x < (ssize_t) image->columns; x++)
2910 {
2911 *q++=(JSAMPLE) (ScaleQuantumToShort(ClampToQuantum(GetPixelLuma(image,
2912 p)))/scale);
2913 p+=GetPixelChannels(image);
2914 }
2915 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2916 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2917 image->rows);
2918 if (status == MagickFalse)
2919 break;
2920 }
2921 else
2922 if ((jpeg_info->in_color_space == JCS_RGB) ||
2923 (jpeg_info->in_color_space == JCS_YCbCr))
2924 for (y=0; y < (ssize_t) image->rows; y++)
2925 {
2926 const Quantum
2927 *p;
2928
2929 ssize_t
2930 x;
2931
2932 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2933 if (p == (const Quantum *) NULL)
2934 break;
2935 q=jpeg_pixels;
2936 for (x=0; x < (ssize_t) image->columns; x++)
2937 {
2938 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelRed(image,p))/scale);
2939 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelGreen(image,p))/scale);
2940 *q++=(JSAMPLE) (ScaleQuantumToShort(GetPixelBlue(image,p))/scale);
2941 p+=GetPixelChannels(image);
2942 }
2943 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2944 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2945 image->rows);
2946 if (status == MagickFalse)
2947 break;
2948 }
2949 else
2950 for (y=0; y < (ssize_t) image->rows; y++)
2951 {
2952 const Quantum
2953 *p;
2954
2955 ssize_t
2956 x;
2957
2958 p=GetVirtualPixels(image,0,y,image->columns,1,exception);
2959 if (p == (const Quantum *) NULL)
2960 break;
2961 q=jpeg_pixels;
2962 for (x=0; x < (ssize_t) image->columns; x++)
2963 {
2964 /*
2965 Convert DirectClass packets to contiguous CMYK scanlines.
2966 */
2967 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelRed(
2968 image,p))/scale);
2969 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelGreen(
2970 image,p))/scale);
2971 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlue(
2972 image,p))/scale);
2973 *q++=(JSAMPLE) (ScaleQuantumToShort(QuantumRange-GetPixelBlack(
2974 image,p))/scale);
2975 p+=GetPixelChannels(image);
2976 }
2977 (void) jpeg_write_scanlines(jpeg_info,scanline,1);
2978 status=SetImageProgress(image,SaveImageTag,(MagickOffsetType) y,
2979 image->rows);
2980 if (status == MagickFalse)
2981 break;
2982 }
2983 if (y == (ssize_t) image->rows)
2984 jpeg_finish_compress(jpeg_info);
2985 /*
2986 Relinquish resources.
2987 */
2988 jpeg_destroy_compress(jpeg_info);
2989 memory_info=RelinquishVirtualMemory(memory_info);
2990 (void) CloseBlob(image);
2991 return(MagickTrue);
2992 }
2993
WriteJPEGImage(const ImageInfo * image_info,Image * image,ExceptionInfo * exception)2994 static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
2995 Image *image,ExceptionInfo *exception)
2996 {
2997 struct jpeg_compress_struct
2998 jpeg_info;
2999
3000 return(WriteJPEGImage_(image_info,image,&jpeg_info,exception));
3001 }
3002 #endif
3003