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