]> granicus.if.org Git - imagemagick/blobdiff - coders/jpeg.c
(no commit message)
[imagemagick] / coders / jpeg.c
index 17e090e88cb84f1f6968cccb8cb8e95f0a78adea..d28d4583874daeac25de165241f37092b2e29227 100644 (file)
@@ -17,7 +17,7 @@
 %                                 July 1992                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2009 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2010 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  %
@@ -61,6 +61,7 @@
 #include "magick/log.h"
 #include "magick/magick.h"
 #include "magick/memory_.h"
+#include "magick/module.h"
 #include "magick/monitor.h"
 #include "magick/monitor-private.h"
 #include "magick/option.h"
@@ -71,7 +72,7 @@
 #include "magick/splay-tree.h"
 #include "magick/static.h"
 #include "magick/string_.h"
-#include "magick/module.h"
+#include "magick/string-private.h"
 #include "magick/utility.h"
 #include <setjmp.h>
 #if defined(MAGICKCORE_JPEG_DELEGATE)
@@ -272,6 +273,26 @@ static void InitializeSource(j_decompress_ptr cinfo)
   source->start_of_blob=TRUE;
 }
 
+static MagickBooleanType IsITUFaxImage(const Image *image)
+{
+  const StringInfo
+    *profile;
+
+  const unsigned char
+    *datum;
+
+  profile=GetImageProfile(image,"8bim");
+  if (profile == (const StringInfo *) NULL)
+    return(MagickFalse);
+  if (GetStringInfoLength(profile) < 5)
+    return(MagickFalse);
+  datum=GetStringInfoDatum(profile);
+  if ((datum[0] == 0x47) && (datum[1] == 0x33) && (datum[2] == 0x46) &&
+      (datum[3] == 0x41) && (datum[4] == 0x58))
+    return(MagickTrue);
+  return(MagickFalse);
+}
+
 static void JPEGErrorHandler(j_common_ptr jpeg_info)
 {
   ErrorManager
@@ -710,17 +731,12 @@ static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
          {
            if ((qvalue < hash[i]) && (sum < sums[i]))
              continue;
-           if ((qvalue <= hash[i]) && (sum <= sums[i]))
+           if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
              image->quality=(unsigned long) i+1;
            if (image->debug != MagickFalse)
-             {
-               if (image->quality != UndefinedCompressionQuality)
-                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                   "Quality: %ld",image->quality);
-               else
-                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                   "Quality: %ld (approximate)",i+1);
-             }
+             (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+               "Quality: %ld (%s)",i+1,(qvalue <= hash[i]) &&
+               (sum <= sums[i]) ? "exact" : "approximate");
            break;
          }
        }
@@ -764,16 +780,12 @@ static void JPEGSetImageQuality(struct jpeg_decompress_struct *jpeg_info,
            {
              if ((qvalue < hash[i]) && (sum < sums[i]))
                continue;
-             image->quality=(unsigned long) i+1;
+             if (((qvalue <= hash[i]) && (sum <= sums[i])) || (i >= 50))
+               image->quality=(unsigned long) i+1;
              if (image->debug != MagickFalse)
-               {
-                 if ((qvalue > hash[i]) || (sum > sums[i]))
-                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                     "Quality: %ld (approximate)",i+1);
-                 else
-                   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                     "Quality: %ld",i+1);
-               }
+               (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                 "Quality: %ld (%s)",i+1,(qvalue <= hash[i]) &&
+                 (sum <= sums[i]) ? "exact" : "approximate");
              break;
            }
          }
@@ -799,7 +811,7 @@ static void JPEGSetImageSamplingFactor(struct jpeg_decompress_struct *jpeg_info,
         jpeg_info->comp_info[2].v_samp_factor,
         jpeg_info->comp_info[3].h_samp_factor,
         jpeg_info->comp_info[3].v_samp_factor);
-        break;
+      break;
     }
     case JCS_GRAYSCALE:
     {
@@ -855,12 +867,12 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
   ErrorManager
     error_manager;
 
-  IndexPacket
-    jindex;
-
   Image
     *image;
 
+  IndexPacket
+    index;
+
   long
     y;
 
@@ -924,11 +936,11 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
   if (setjmp(error_manager.error_recovery) != 0)
     {
       jpeg_destroy_decompress(&jpeg_info);
-      InheritException(exception,&image->exception);
       (void) CloseBlob(image);
       number_pixels=(MagickSizeType) image->columns*image->rows;
       if (number_pixels != 0)
         return(GetFirstImageInList(image));
+      InheritException(exception,&image->exception);
       return(DestroyImage(image));
     }
   jpeg_info.client_data=(void *) &error_manager;
@@ -945,6 +957,14 @@ 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.
   */
@@ -970,7 +990,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       GeometryInfo
         geometry_info;
 
-      int
+      MagickStatusType
         flags;
 
       /*
@@ -1025,6 +1045,50 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       jpeg_info.quantize_colors=MagickTrue;
       jpeg_info.desired_number_of_colors=(int) image_info->colors;
     }
+  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;
+    }
+  option=GetImageOption(image_info,"jpeg:dct-method");
+  if (option != (const char *) NULL)
+    switch (*option)
+    {
+      case 'D':
+      case 'd':
+      {
+        if (LocaleCompare(option,"default") == 0)
+          jpeg_info.dct_method=JDCT_DEFAULT;
+        break;
+      }
+      case 'F':
+      case 'f':
+      {
+        if (LocaleCompare(option,"fastest") == 0)
+          jpeg_info.dct_method=JDCT_FASTEST;
+        if (LocaleCompare(option,"float") == 0)
+          jpeg_info.dct_method=JDCT_FLOAT;
+        break;
+      }
+      case 'I':
+      case 'i':
+      {
+        if (LocaleCompare(option,"ifast") == 0)
+          jpeg_info.dct_method=JDCT_IFAST;
+        if (LocaleCompare(option,"islow") == 0)
+          jpeg_info.dct_method=JDCT_ISLOW;
+        break;
+      }
+    }
+  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;
+    }
   (void) jpeg_start_decompress(&jpeg_info);
   image->columns=jpeg_info.output_width;
   image->rows=jpeg_info.output_height;
@@ -1082,7 +1146,6 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       if (jpeg_pixels != (unsigned char *) NULL)
         jpeg_pixels=(unsigned char *) RelinquishMagickMemory(jpeg_pixels);
       jpeg_destroy_decompress(&jpeg_info);
-      InheritException(exception,&image->exception);
       (void) CloseBlob(image);
       number_pixels=(MagickSizeType) image->columns*image->rows;
       if (number_pixels != 0)
@@ -1113,13 +1176,13 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
   for (y=0; y < (long) image->rows; y++)
   {
     register IndexPacket
-      *__restrict indexes;
+      *restrict indexes;
 
     register long
       x;
 
     register PixelPacket
-      *__restrict q;
+      *restrict q;
 
     if (jpeg_read_scanlines(&jpeg_info,scanline,1) != 1)
       {
@@ -1144,9 +1207,9 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
               pixel=(unsigned long) GETJSAMPLE(*p);
             else
               pixel=(unsigned long) ((GETJSAMPLE(*p) ^ 0x80) << 4);
-            jindex=ConstrainColormapIndex(image,pixel);
-            indexes[x]=jindex;
-            *q++=image->colormap[(int) jindex];
+            index=ConstrainColormapIndex(image,pixel);
+            indexes[x]=index;
+            *q++=image->colormap[(int) index];
             p++;
           }
         else
@@ -1159,7 +1222,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
                 (GETJSAMPLE(*p++) << 4));
               q->blue=ScaleShortToQuantum((unsigned char)
                 (GETJSAMPLE(*p++) << 4));
-              q->opacity=OpaqueOpacity;
+              SetOpacityPixelComponent(q,OpaqueOpacity);
               q++;
             }
           else
@@ -1171,7 +1234,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
                 (unsigned char) (GETJSAMPLE(*p++) << 4));
               q->blue=(Quantum) QuantumRange-ScaleShortToQuantum((unsigned char)
                 (GETJSAMPLE(*p++) << 4));
-              q->opacity=OpaqueOpacity;
+              SetOpacityPixelComponent(q,OpaqueOpacity);
               indexes[x]=(IndexPacket) QuantumRange-ScaleShortToQuantum(
                 (unsigned char) (GETJSAMPLE(*p++) << 4));
               q++;
@@ -1181,9 +1244,9 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
       if (jpeg_info.output_components == 1)
         for (x=0; x < (long) image->columns; x++)
         {
-          jindex=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p));
-          indexes[x]=(IndexPacket) jindex;
-          *q++=image->colormap[(int) jindex];
+          index=ConstrainColormapIndex(image,(unsigned long) GETJSAMPLE(*p));
+          indexes[x]=(IndexPacket) index;
+          *q++=image->colormap[(int) index];
           p++;
         }
       else
@@ -1193,7 +1256,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
             q->red=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
             q->green=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
             q->blue=ScaleCharToQuantum((unsigned char) GETJSAMPLE(*p++));
-            q->opacity=OpaqueOpacity;
+            SetOpacityPixelComponent(q,OpaqueOpacity);
             q++;
           }
         else
@@ -1205,7 +1268,7 @@ static Image *ReadJPEGImage(const ImageInfo *image_info,
               GETJSAMPLE(*p++));
             q->blue=(Quantum) QuantumRange-ScaleCharToQuantum((unsigned char)
               GETJSAMPLE(*p++));
-            q->opacity=OpaqueOpacity;
+            SetOpacityPixelComponent(q,OpaqueOpacity);
             indexes[x]=(IndexPacket) QuantumRange-ScaleCharToQuantum(
               (unsigned char) GETJSAMPLE(*p++));
             q++;
@@ -1694,7 +1757,11 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
       break;
     }
     default:
+    {
+      if (image->colorspace != RGBColorspace)
+        (void) TransformImageColorspace(image,RGBColorspace);
       break;
+    }
   }
   if ((image_info->type != TrueColorType) &&
       (IsGrayImage(image,&image->exception) != MagickFalse))
@@ -1799,6 +1866,49 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
       "Interlace: nonprogressive");
 #endif
+  option=GetImageOption(image_info,"jpeg:extent");
+  if (option != (const char *) NULL)
+    {
+      Image
+        *jpeg_image;
+
+      ImageInfo
+        *jpeg_info;
+
+      jpeg_info=CloneImageInfo(image_info);
+      jpeg_image=CloneImage(image,0,0,MagickTrue,&image->exception);
+      if (jpeg_image != (Image *) NULL)
+        {
+          MagickSizeType
+            extent;
+
+          size_t
+            maximum,
+            minimum;
+
+          /*
+            Search for compression quality that does not exceed image extent.
+          */
+          jpeg_info->quality=0;
+          extent=(MagickSizeType) SiPrefixToDouble(option,100.0);
+          (void) DeleteImageOption(jpeg_info,"jpeg:extent");
+          (void) AcquireUniqueFilename(jpeg_image->filename);
+          maximum=101;
+          for (minimum=0; minimum != maximum; )
+          {
+            jpeg_image->quality=minimum+(maximum-minimum)/2;
+            status=WriteJPEGImage(jpeg_info,jpeg_image);
+            if (GetBlobSize(jpeg_image) <= extent)
+              minimum=jpeg_image->quality+1;
+            else
+              maximum=jpeg_image->quality-1;
+          }
+          (void) RelinquishUniqueFileResource(jpeg_image->filename);
+          image->quality=minimum-1;
+          jpeg_image=DestroyImage(jpeg_image);
+        }
+      jpeg_info=DestroyImageInfo(jpeg_info);
+    }
   if ((image_info->compression != LosslessJPEGCompression) &&
       (image->quality <= 100))
     {
@@ -2089,9 +2199,9 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
             q=jpeg_pixels;
             for (x=0; x < (long) image->columns; x++)
             {
-              *q++=(JSAMPLE) (ScaleQuantumToShort(p->red) >> 4);
-              *q++=(JSAMPLE) (ScaleQuantumToShort(p->green) >> 4);
-              *q++=(JSAMPLE) (ScaleQuantumToShort(p->blue) >> 4);
+              *q++=(JSAMPLE) (ScaleQuantumToShort(GetRedPixelComponent(p)) >> 4);
+              *q++=(JSAMPLE) (ScaleQuantumToShort(GetGreenPixelComponent(p)) >> 4);
+              *q++=(JSAMPLE) (ScaleQuantumToShort(GetBluePixelComponent(p)) >> 4);
               p++;
             }
             (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
@@ -2121,9 +2231,9 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
               /*
                 Convert DirectClass packets to contiguous CMYK scanlines.
               */
-              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->red) >> 4));
-              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->green) >> 4));
-              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(p->blue) >> 4));
+              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(GetRedPixelComponent(p)) >> 4));
+              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(GetGreenPixelComponent(p)) >> 4));
+              *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(GetBluePixelComponent(p)) >> 4));
               *q++=(JSAMPLE) (4095-(ScaleQuantumToShort(indexes[x]) >> 4));
               p++;
             }
@@ -2173,9 +2283,9 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
           q=jpeg_pixels;
           for (x=0; x < (long) image->columns; x++)
           {
-            *q++=(JSAMPLE) ScaleQuantumToChar(p->red);
-            *q++=(JSAMPLE) ScaleQuantumToChar(p->green);
-            *q++=(JSAMPLE) ScaleQuantumToChar(p->blue);
+            *q++=(JSAMPLE) ScaleQuantumToChar(GetRedPixelComponent(p));
+            *q++=(JSAMPLE) ScaleQuantumToChar(GetGreenPixelComponent(p));
+            *q++=(JSAMPLE) ScaleQuantumToChar(GetBluePixelComponent(p));
             p++;
           }
           (void) jpeg_write_scanlines(&jpeg_info,scanline,1);
@@ -2205,11 +2315,11 @@ static MagickBooleanType WriteJPEGImage(const ImageInfo *image_info,
               Convert DirectClass packets to contiguous CMYK scanlines.
             */
             *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-              p->red)));
+              GetRedPixelComponent(p))));
             *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-              p->green)));
+              GetGreenPixelComponent(p))));
             *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
-              p->blue)));
+              GetBluePixelComponent(p))));
             *q++=(JSAMPLE) (ScaleQuantumToChar((Quantum) (QuantumRange-
               indexes[x])));
             p++;