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