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