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