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