]> granicus.if.org Git - imagemagick/blobdiff - coders/jpeg.c
(no commit message)
[imagemagick] / coders / jpeg.c
index f8b76745d46a24bc224b6df961af332e14c680e6..96f1bce4f4e68ef8e9df073f42b780d55bfe0c02 100644 (file)
@@ -17,7 +17,7 @@
 %                                 July 1992                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 #include "MagickCore/static.h"
 #include "MagickCore/string_.h"
 #include "MagickCore/string-private.h"
+#include "MagickCore/token.h"
 #include "MagickCore/utility.h"
 #include "MagickCore/xml-tree.h"
 #include "MagickCore/xml-tree-private.h"
 #include <setjmp.h>
 #if defined(MAGICKCORE_JPEG_DELEGATE)
 #define JPEG_INTERNAL_OPTIONS
-#if defined(__MINGW32__)
+#if defined(__MINGW32__) || defined(__MINGW64__)
 # define XMD_H 1  /* Avoid conflicting typedef for INT32 */
-typedef unsigned char boolean;
-#define HAVE_BOOLEAN
 #endif
 #undef HAVE_STDLIB_H
 #include "jpeglib.h"
@@ -100,7 +99,7 @@ typedef unsigned char boolean;
 #define ICC_PROFILE  "ICC_PROFILE"
 #define IPTC_MARKER  (JPEG_APP0+13)
 #define XML_MARKER  (JPEG_APP0+1)
-#define MaxBufferExtent  8192
+#define MaxBufferExtent  16384
 \f
 /*
   Typedef declarations.
@@ -129,6 +128,9 @@ typedef struct _ErrorManager
   MagickBooleanType
     finished;
 
+  StringInfo
+    *profile;
+
   jmp_buf
     error_recovery;
 } ErrorManager;
@@ -159,8 +161,10 @@ typedef struct _QuantizationTable
     width,
     height;
 
+  double
+    divisor;
+
   unsigned int
-    divisor,
     *levels;
 } QuantizationTable;
 \f
@@ -245,7 +249,7 @@ static boolean FillInputBuffer(j_decompress_ptr cinfo)
     MaxBufferExtent,source->buffer);
   if (source->manager.bytes_in_buffer == 0)
     {
-      if (source->start_of_blob != 0)
+      if (source->start_of_blob != FALSE)
         ERREXIT(cinfo,JERR_INPUT_EMPTY);
       WARNMS(cinfo,JWRN_JPEG_EOF);
       source->buffer[0]=(JOCTET) 0xff;
@@ -327,6 +331,8 @@ static void JPEGErrorHandler(j_common_ptr jpeg_info)
 
 static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
 {
+#define JPEGExcessiveWarnings  1000
+
   char
     message[JMSG_LENGTH_MAX];
 
@@ -349,11 +355,10 @@ static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
         Process warning message.
       */
       (jpeg_info->err->format_message)(jpeg_info,message);
-      if ((jpeg_info->err->num_warnings == 0) ||
-          (jpeg_info->err->trace_level >= 3))
-        ThrowBinaryException(CorruptImageWarning,(char *) message,
-          image->filename);
-      jpeg_info->err->num_warnings++;
+      if (jpeg_info->err->num_warnings++ > JPEGExcessiveWarnings)
+        JPEGErrorHandler(jpeg_info);
+      ThrowBinaryException(CorruptImageWarning,(char *) message,
+        image->filename);
     }
   else
     if ((image->debug != MagickFalse) &&
@@ -371,9 +376,6 @@ static MagickBooleanType JPEGWarningHandler(j_common_ptr jpeg_info,int level)
 
 static boolean ReadComment(j_decompress_ptr jpeg_info)
 {
-  char
-    *comment;
-
   ErrorManager
     *error_manager;
 
@@ -383,7 +385,7 @@ static boolean ReadComment(j_decompress_ptr jpeg_info)
   Image
     *image;
 
-  register char
+  register unsigned char
     *p;
 
   register ssize_t
@@ -392,6 +394,9 @@ static boolean ReadComment(j_decompress_ptr jpeg_info)
   size_t
     length;
 
+  StringInfo
+    *comment;
+
   /*
     Determine length of comment.
   */
@@ -403,22 +408,22 @@ static boolean ReadComment(j_decompress_ptr jpeg_info)
   length-=2;
   if (length <= 0)
     return(MagickTrue);
-  comment=(char *) NULL;
-  if (~length >= (MaxTextExtent-1))
-    comment=(char *) AcquireQuantumMemory(length+MaxTextExtent,
-      sizeof(*comment));
-  if (comment == (char *) NULL)
+  comment=BlobToStringInfo((const void *) NULL,length);
+  if (comment == (StringInfo *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
   /*
     Read comment.
   */
-  i=(ssize_t) length-1;
-  for (p=comment; i-- >= 0; p++)
-    *p=(char) GetCharacter(jpeg_info);
+  error_manager->profile=comment;
+  p=GetStringInfoDatum(comment);
+  for (i=0; i < (ssize_t) GetStringInfoLength(comment); i++)
+    *p++=(unsigned char) GetCharacter(jpeg_info);
   *p='\0';
-  (void) SetImageProperty(image,"comment",comment,exception);
-  comment=DestroyString(comment);
+  error_manager->profile=NULL;
+  p=GetStringInfoDatum(comment);
+  (void) SetImageProperty(image,"comment",(const char *) p,exception);
+  comment=DestroyStringInfo(comment);
   return(MagickTrue);
 }
 
@@ -485,9 +490,11 @@ static boolean ReadICCProfile(j_decompress_ptr jpeg_info)
   if (profile == (StringInfo *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
+  error_manager->profile=profile;
   p=GetStringInfoDatum(profile);
   for (i=(ssize_t) GetStringInfoLength(profile)-1; i >= 0; i--)
     *p++=(unsigned char) GetCharacter(jpeg_info);
+  error_manager->profile=NULL;
   icc_profile=(StringInfo *) GetImageProfile(image,"icc");
   if (icc_profile != (StringInfo *) NULL)
     {
@@ -585,9 +592,11 @@ static boolean ReadIPTCProfile(j_decompress_ptr jpeg_info)
   if (profile == (StringInfo *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
+  error_manager->profile=profile;
   p=GetStringInfoDatum(profile);
   for (i=0;  i < (ssize_t) GetStringInfoLength(profile); i++)
     *p++=(unsigned char) GetCharacter(jpeg_info);
+  error_manager->profile=NULL;
   iptc_profile=(StringInfo *) GetImageProfile(image,"8bim");
   if (iptc_profile != (StringInfo *) NULL)
     {
@@ -660,9 +669,11 @@ static boolean ReadProfile(j_decompress_ptr jpeg_info)
   if (profile == (StringInfo *) NULL)
     ThrowBinaryException(ResourceLimitError,"MemoryAllocationFailed",
       image->filename);
+  error_manager->profile=profile;
   p=GetStringInfoDatum(profile);
   for (i=0; i < (ssize_t) GetStringInfoLength(profile); i++)
     *p++=(unsigned char) GetCharacter(jpeg_info);
+  error_manager->profile=NULL;
   if (marker == 1)
     {
       p=GetStringInfoDatum(profile);
@@ -973,7 +984,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
     *image;
 
   JSAMPLE
-    *jpeg_pixels;
+    *volatile jpeg_pixels;
 
   JSAMPROW
     scanline[1];
@@ -1041,6 +1052,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
   if (setjmp(error_manager.error_recovery) != 0)
     {
       jpeg_destroy_decompress(&jpeg_info);
+      if (error_manager.profile != (StringInfo *) NULL)
+        error_manager.profile=DestroyStringInfo(error_manager.profile);
       (void) CloseBlob(image);
       number_pixels=(MagickSizeType) image->columns*image->rows;
       if (number_pixels != 0)
@@ -1061,14 +1074,6 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       (image_info->colorspace == Rec601YCbCrColorspace) ||
       (image_info->colorspace == Rec709YCbCrColorspace))
     jpeg_info.out_color_space=JCS_YCbCr;
-  if (IsITUFaxImage(image) != MagickFalse)
-    {
-      image->colorspace=LabColorspace;
-      jpeg_info.out_color_space=JCS_YCbCr;
-    }
-  else
-    if (jpeg_info.out_color_space == JCS_CMYK)
-      image->colorspace=CMYKColorspace;
   /*
     Set image resolution.
   */
@@ -1151,12 +1156,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       jpeg_info.desired_number_of_colors=(int) StringToUnsignedLong(option);
     }
   option=GetImageOption(image_info,"jpeg:block-smoothing");
-  if (option != (const char *) NULL)
-    {
-      jpeg_info.do_block_smoothing=MagickFalse;
-      if (IsMagickTrue(option) != MagickFalse)
-        jpeg_info.do_block_smoothing=MagickTrue;
-    }
+  jpeg_info.do_block_smoothing=IsStringTrue(option);
   jpeg_info.dct_method=JDCT_FLOAT;
   option=GetImageOption(image_info,"jpeg:dct-method");
   if (option != (const char *) NULL)
@@ -1189,20 +1189,40 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       }
     }
   option=GetImageOption(image_info,"jpeg:fancy-upsampling");
-  if (option != (const char *) NULL)
-    {
-      jpeg_info.do_fancy_upsampling=MagickFalse;
-      if (IsMagickTrue(option) != MagickFalse)
-        jpeg_info.do_fancy_upsampling=MagickTrue;
-    }
+  jpeg_info.do_fancy_upsampling=IsStringTrue(option);
   (void) jpeg_start_decompress(&jpeg_info);
   image->columns=jpeg_info.output_width;
   image->rows=jpeg_info.output_height;
   image->depth=(size_t) jpeg_info.data_precision;
-  if (jpeg_info.out_color_space == JCS_YCbCr)
-    image->colorspace=YCbCrColorspace;
-  if (jpeg_info.out_color_space == JCS_CMYK)
-    image->colorspace=CMYKColorspace;
+  switch (jpeg_info.out_color_space)
+  {
+    case JCS_RGB:
+    default:
+    {
+      (void) SetImageColorspace(image,sRGBColorspace,exception);
+      break;
+    }
+    case JCS_GRAYSCALE:
+    {
+      (void) SetImageColorspace(image,GRAYColorspace,exception);
+      break;
+    }
+    case JCS_YCbCr:
+    {
+      (void) SetImageColorspace(image,YCbCrColorspace,exception);
+      break;
+    }
+    case JCS_CMYK:
+    {
+      (void) SetImageColorspace(image,CMYKColorspace,exception);
+      break;
+    }
+  }
+  if (IsITUFaxImage(image) != MagickFalse)
+    {
+      (void) SetImageColorspace(image,LabColorspace,exception);
+      jpeg_info.out_color_space=JCS_YCbCr;
+    }
   option=GetImageOption(image_info,"jpeg:colors");
   if (option != (const char *) NULL)
     if (AcquireImageColormap(image,StringToUnsignedLong(option),exception)
@@ -1251,8 +1271,8 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
   */
   if (setjmp(error_manager.error_recovery) != 0)
     {
-      if (jpeg_pixels != (unsigned char *) NULL)
-        jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
+      if (jpeg_pixels != (JSAMPLE *) NULL)
+        jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
       jpeg_destroy_decompress(&jpeg_info);
       (void) CloseBlob(image);
       number_pixels=(MagickSizeType) image->columns*image->rows;
@@ -1406,7 +1426,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
     Free jpeg resources.
   */
   jpeg_destroy_decompress(&jpeg_info);
-  jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
+  jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
   (void) CloseBlob(image);
   return(GetFirstImageInList(image));
 }
@@ -1715,8 +1735,8 @@ static QuantizationTable *GetQuantizationTable(const char *filename,
       xml=DestroyString(xml);
       return(table);
     }
-  table->divisor=(unsigned int) StringToLong(attribute);
-  if (table->divisor < 1)
+  table->divisor=InterpretLocaleValue(attribute,(char **) NULL);
+  if (table->divisor == 0.0)
     {
       (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
         "XmlInvalidAttribute", "<levels divisor>, table \"%s\"",slot);
@@ -1745,18 +1765,15 @@ static QuantizationTable *GetQuantizationTable(const char *filename,
       "UnableToAcquireQuantizationTable");
   for (i=0; i < (ssize_t) (table->width*table->height); i++)
   {
-    table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)+0.5);
-    if (table->divisor != 1)
-      table->levels[i]/=table->divisor;
+    table->levels[i]=(unsigned int) (InterpretLocaleValue(content,&p)/
+      table->divisor+0.5);
     while (isspace((int) ((unsigned char) *p)) != 0)
       p++;
     if (*p == ',')
       p++;
     content=p;
   }
-  for (j=i; i < 64; i++)
-    table->levels[j]=table->levels[j-1];
-  value=(double) strtol(content,&p,10);
+  value=InterpretLocaleValue(content,&p);
   (void) value;
   if (p != content)
     {
@@ -1767,6 +1784,8 @@ static QuantizationTable *GetQuantizationTable(const char *filename,
      xml=DestroyString(xml);
      return(table);
    }
+  for (j=i; j < 64; j++)
+    table->levels[j]=table->levels[j-1];
   quantization_tables=DestroyXMLTree(quantization_tables);
   xml=DestroyString(xml);
   return(table);
@@ -2005,7 +2024,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
     quality;
 
   JSAMPLE
-    *jpeg_pixels;
+    *volatile jpeg_pixels;
 
   JSAMPROW
     scanline[1];
@@ -2088,8 +2107,6 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
       break;
     }
     case GRAYColorspace:
-    case Rec601LumaColorspace:
-    case Rec709LumaColorspace:
     {
       jpeg_info.input_components=1;
       jpeg_info.in_color_space=JCS_GRAYSCALE;
@@ -2097,7 +2114,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
     }
     default:
     {
-      if (IsRGBColorspace(image->colorspace) == MagickFalse)
+      if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
         (void) TransformImageColorspace(image,sRGBColorspace,exception);
       break;
     }
@@ -2109,6 +2126,8 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
       jpeg_info.in_color_space=JCS_GRAYSCALE;
     }
   jpeg_set_defaults(&jpeg_info);
+  if (jpeg_info.in_color_space == JCS_CMYK)
+    jpeg_set_colorspace(&jpeg_info,JCS_YCCK);
   if ((jpeg_info.data_precision != 12) && (image->depth <= 8))
     jpeg_info.data_precision=8;
   else
@@ -2165,11 +2184,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
     }
   option=GetImageOption(image_info,"jpeg:optimize-coding");
   if (option != (const char *) NULL)
-    {
-      jpeg_info.optimize_coding=MagickFalse;
-      if (IsMagickTrue(option) != MagickFalse)
-        jpeg_info.optimize_coding=MagickTrue;
-    }
+    jpeg_info.optimize_coding=IsStringTrue(option);
   else
     {
       MagickSizeType
@@ -2183,9 +2198,8 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
             Perform optimization only if available memory resources permit it.
           */
           status=AcquireMagickResource(MemoryResource,length);
-          if (status != MagickFalse)
-            jpeg_info.optimize_coding=MagickTrue;
           RelinquishMagickResource(MemoryResource,length);
+          jpeg_info.optimize_coding=status;
         }
     }
 #if (JPEG_LIB_VERSION >= 61) && defined(C_PROGRESSIVE_SUPPORTED)
@@ -2291,8 +2305,34 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
 #endif
     }
   jpeg_set_quality(&jpeg_info,quality,MagickTrue);
+#if (JPEG_LIB_VERSION >= 70)
+  option=GetImageOption(image_info,"quality");
+  if (option != (const char *) NULL)
+    {
+      GeometryInfo
+        geometry_info;
+
+      int
+        flags;
+
+      /*
+        Set quality scaling for luminance and chrominance separately.
+      */
+      flags=ParseGeometry(option,&geometry_info);
+      if (((flags & RhoValue) != 0) && ((flags & SigmaValue) != 0))
+        {
+          jpeg_info.q_scale_factor[0]=jpeg_quality_scaling((int)
+            (geometry_info.rho+0.5));
+          jpeg_info.q_scale_factor[1]=jpeg_quality_scaling((int)
+            (geometry_info.sigma+0.5));
+          jpeg_default_qtables(&jpeg_info,MagickTrue);
+        }
+    }
+#endif
   sampling_factor=(const char *) NULL;
-  value=GetImageProperty(image,"jpeg:sampling-factor",exception);
+  value=GetImageOption(image_info,"jpeg:sampling-factor");
+  if (value == (char *) NULL)
+    value=GetImageProperty(image,"jpeg:sampling-factor",exception);
   if (value != (char *) NULL)
     {
       sampling_factor=value;
@@ -2451,8 +2491,6 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
           break;
         }
         case GRAYColorspace:
-        case Rec601LumaColorspace:
-        case Rec709LumaColorspace:
         {
           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
             "Colorspace: GRAY");
@@ -2529,8 +2567,8 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
   if (setjmp(error_manager.error_recovery) != 0)
     {
       jpeg_destroy_compress(&jpeg_info);
-      if (jpeg_pixels != (unsigned char *) NULL)
-        jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
+      if (jpeg_pixels != (JSAMPLE *) NULL)
+        jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
       (void) CloseBlob(image);
       return(MagickFalse);
     }
@@ -2608,11 +2646,11 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
                 Convert DirectClass packets to contiguous CMYK scanlines.
               */
               *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-                GetPixelRed(image,p))));
+                GetPixelCyan(image,p))));
               *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-                GetPixelGreen(image,p))));
+                GetPixelMagenta(image,p))));
               *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-                GetPixelBlue(image,p))));
+                GetPixelYellow(image,p))));
               *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
                 GetPixelBlack(image,p))));
               p+=GetPixelChannels(image);
@@ -2717,7 +2755,7 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
     Relinquish resources.
   */
   jpeg_destroy_compress(&jpeg_info);
-  jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
+  jpeg_pixels=(JSAMPLE *) RelinquishMagickMemory(jpeg_pixels);
   (void) CloseBlob(image);
   return(MagickTrue);
 }