source: ImageMagick/trunk/coders/jpeg.c @ 6934

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