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