source: ImageMagick/branches/ImageMagick-6/coders/jpeg.c @ 7224

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