GraphicsMagick: ReadJPEGImage(): Support libjpeg-turbo 3.X APIs ...

GraphicsMagick Commits <[email protected]> Sat, 10 Feb 2024 14:54:57 -0600
Newsgroups gmane.comp.video.graphicsmagick.cvs
Message-ID <mailman.10771.1707598515.16032.graphicsmagick-commit@lists.sourceforge.net>
changeset 0bdf6df7faf2 in /hg/GraphicsMagick
details: http://hg.GraphicsMagick.org/hg/GraphicsMagick?cmd=changeset;node=0bdf6df7faf2
summary: ReadJPEGImage(): Support libjpeg-turbo 3.X APIs to read deep JPEG.

diffstat:

 ChangeLog                              |   10 +
 VisualMagick/installer/inc/version.isx |    4 +-
 coders/jpeg.c                          |  713 ++++++++++++++++++++------------
 coders/tiff.c                          |   56 +-
 magick/version.h                       |    4 +-
 www/ChangeLog.html                     |   10 +
 6 files changed, 496 insertions(+), 301 deletions(-)

diffs (truncated from 1236 to 500 lines):

diff -r fc5cac9afb5c -r 0bdf6df7faf2 ChangeLog
--- a/ChangeLog	Tue Feb 06 08:26:53 2024 -0600
+++ b/ChangeLog	Sat Feb 10 14:54:39 2024 -0600
@@ -1,3 +1,13 @@
+2024-02-10  Bob Friesenhahn  <[email protected]>
+
+	* coders/jpeg.c (WriteJPEGImage): Do some preparation to implement
+	support for libjpeg-turbo 3.X APIs while writing deep JPEG.
+
+	* coders/tiff.c (AddIFDExifFields): Fix a compilation warning.
+
+	* coders/jpeg.c (ReadJPEGImage): Support libjpeg-turbo 3.X APIs to
+	read deep JPEG.
+
 2024-02-06  Bob Friesenhahn  <[email protected]>
 
 	* coders/tiff.c (AddIFDExifFields): Add casts to prevent signed vs
diff -r fc5cac9afb5c -r 0bdf6df7faf2 VisualMagick/installer/inc/version.isx
--- a/VisualMagick/installer/inc/version.isx	Tue Feb 06 08:26:53 2024 -0600
+++ b/VisualMagick/installer/inc/version.isx	Sat Feb 10 14:54:39 2024 -0600
@@ -10,5 +10,5 @@
 
 #define public MagickPackageName "GraphicsMagick"
 #define public MagickPackageVersion "1.4"
-#define public MagickPackageVersionAddendum ".020240206"
-#define public MagickPackageReleaseDate "snapshot-20240206"
+#define public MagickPackageVersionAddendum ".020240210"
+#define public MagickPackageReleaseDate "snapshot-20240210"
diff -r fc5cac9afb5c -r 0bdf6df7faf2 coders/jpeg.c
--- a/coders/jpeg.c	Tue Feb 06 08:26:53 2024 -0600
+++ b/coders/jpeg.c	Sat Feb 10 14:54:39 2024 -0600
@@ -136,6 +136,26 @@
 static const char xmp_std_header[]="http://ns.adobe.com/xap/1.0/";
 
 
+/*
+  Struct to lessen the impact of multiple sample types
+
+  This assumes a normal architecture where pointer size is consistent.
+*/
+typedef struct
+{
+  union
+  {
+    void *v;
+    JSAMPLE *j;
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+    J12SAMPLE *j12;
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+    J16SAMPLE *j16;
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+  } t;
+} magick_jpeg_pixels_t;
+
 typedef struct _DestinationManager
 {
   struct jpeg_destination_mgr
@@ -178,7 +198,7 @@
   unsigned char
     buffer[65537+200];
 
-  void
+  magick_jpeg_pixels_t
     *jpeg_pixels;
 
 } MagickClientData;
@@ -221,7 +241,7 @@
           MagickFreeMemory(client_data->profiles[i].name);
           MagickFreeResourceLimitedMemory(client_data->profiles[i].info);
         }
-      MagickFreeResourceLimitedMemory(client_data->jpeg_pixels);
+      MagickFreeResourceLimitedMemory(client_data->jpeg_pixels->t.v);
 
       MagickFreeMemory(client_data);
     }
@@ -1248,11 +1268,8 @@
   long
     y;
 
-  JSAMPLE
-    *jpeg_pixels;
-
-  JSAMPROW
-    scanline[1];
+  magick_jpeg_pixels_t
+    jpeg_pixels; /* Contents freed by FreeMagickClientData() */
 
   const char
     *value;
@@ -1269,9 +1286,6 @@
   struct jpeg_decompress_struct
     jpeg_info;
 
-  register JSAMPLE
-    *p;
-
   MagickPassFail
     status;
 
@@ -1299,17 +1313,18 @@
   /*
     Initialize structures.
   */
+  (void) memset(&jpeg_pixels,0,sizeof(jpeg_pixels));
   (void) memset(&jpeg_progress,0,sizeof(jpeg_progress));
   (void) memset(&jpeg_info,0,sizeof(jpeg_info));
   (void) memset(&jpeg_error,0,sizeof(jpeg_error));
   jpeg_info.err=jpeg_std_error(&jpeg_error);
   jpeg_info.err->emit_message=/*(void (*)(j_common_ptr,int))*/ JPEGDecodeMessageHandler;
   jpeg_info.err->error_exit=(void (*)(j_common_ptr)) JPEGErrorHandler;
-  jpeg_pixels=(JSAMPLE *) NULL;
   client_data->image=image;
   client_data->ping=image_info->ping;
   client_data->max_scan_number=100;
   client_data->max_warning_count=MaxWarningCount;
+  client_data->jpeg_pixels=&jpeg_pixels;
 
   /*
     Allow the user to set how many warnings of any given type are
@@ -1699,11 +1714,37 @@
         }
     }
 
-  jpeg_pixels=MagickAllocateResourceLimitedClearedArray(JSAMPLE *,
-                                  jpeg_info.output_components,
-                                  MagickArraySize(image->columns,
-                                                  sizeof(JSAMPLE)));
-  if (jpeg_pixels == (JSAMPLE *) NULL)
+  switch (jpeg_info.data_precision)
+    {
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+    case 16:
+      jpeg_pixels.t.j16 =
+        MagickAllocateResourceLimitedClearedArray(J16SAMPLE *,
+                                                  jpeg_info.output_components,
+                                                  MagickArraySize(image->columns,
+                                                                  sizeof(J16SAMPLE)));
+      break;
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+    case 12:
+      jpeg_pixels.t.j12 =
+        MagickAllocateResourceLimitedClearedArray(J12SAMPLE *,
+                                                  jpeg_info.output_components,
+                                                  MagickArraySize(image->columns,
+                                                                  sizeof(J12SAMPLE)));
+      break;
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+    default:
+      {
+        jpeg_pixels.t.j =
+          MagickAllocateResourceLimitedClearedArray(JSAMPLE *,
+                                                    jpeg_info.output_components,
+                                                    MagickArraySize(image->columns,
+                                                                    sizeof(JSAMPLE)));
+      }
+    }
+
+  if (jpeg_pixels.t.v == (void *) NULL)
     {
       jpeg_destroy_decompress(&jpeg_info);
       ThrowJPEGReaderException(ResourceLimitError,MemoryAllocationFailed,image);
@@ -1717,7 +1758,7 @@
       (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                             "Setjmp return from longjmp!");
       /* Error handling code executed if longjmp was invoked */
-      MagickFreeResourceLimitedMemory(jpeg_pixels);
+      MagickFreeResourceLimitedMemory(jpeg_pixels.t.v);
       jpeg_destroy_decompress(&jpeg_info);
       if (image->exception.severity > exception->severity)
         CopyException(exception,&image->exception);
@@ -1730,10 +1771,10 @@
       return((Image *) NULL);
     }
 
+
   /*
     Convert JPEG pixels to pixel packets.
   */
-  scanline[0]=(JSAMPROW) jpeg_pixels;
   for (y=0; y < (long) image->rows; y++)
     {
       register IndexPacket
@@ -1746,14 +1787,52 @@
         *q;
 
       /*
-        Read scanlines. Stop at first serious error.
-       */
-      if ((jpeg_read_scanlines(&jpeg_info,scanline,1) != 1) ||
-          (image->exception.severity >= ErrorException))
+        Read scanlines (one scanline per cycle). Stop at first serious error.
+      */
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+      if (jpeg_info.data_precision == 16)
         {
-          status=MagickFail;
-          break;
-        }
+          {
+            J16SAMPROW
+              scanline[1];
+
+            scanline[0]=(J16SAMPROW) jpeg_pixels.t.j16;
+            if ((jpeg16_read_scanlines(&jpeg_info, scanline,1) != 1) ||
+                (image->exception.severity >= ErrorException))
+              {
+                status=MagickFail;
+                break;
+              }
+          }
+        } else
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+        if (jpeg_info.data_precision == 12)
+          {
+            J12SAMPROW
+              scanline[1];
+
+            scanline[0]=(J12SAMPROW) jpeg_pixels.t.j12;
+            if ((jpeg12_read_scanlines(&jpeg_info, scanline,1) != 1) ||
+                (image->exception.severity >= ErrorException))
+              {
+                status=MagickFail;
+                break;
+              }
+          } else
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+          {
+            JSAMPROW
+              scanline[1];
+
+            scanline[0]=(JSAMPROW) jpeg_pixels.t.j;
+            if ((jpeg_read_scanlines(&jpeg_info, scanline,1) != 1) ||
+                (image->exception.severity >= ErrorException))
+              {
+                status=MagickFail;
+                break;
+              }
+          }
 
       q=SetImagePixels(image,0,y,image->columns,1);
       if (q == (PixelPacket *) NULL)
@@ -1763,80 +1842,159 @@
         }
       indexes=AccessMutableIndexes(image);
 
-      p=jpeg_pixels;
-
       if (jpeg_info.output_components == 1)
         {
           if (image->storage_class == PseudoClass)
             {
-              for (x=0; x < (long) image->columns; x++)
+              switch(jpeg_info.data_precision)
                 {
-                  index=(IndexPacket) (GETJSAMPLE(*p++));
-                  VerifyColormapIndex(image,index);
-                  indexes[x]=index;
-                  *q++=image->colormap[index];
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+                case 16:
+                  {
+                    for (x=0; x < (long) image->columns; x++)
+                    {
+                      index=(IndexPacket) ScaleQuantumToIndex((ScaleShortToQuantum(jpeg_pixels.t.j16[x])));
+                      VerifyColormapIndex(image,index);
+                      indexes[x]=index;
+                      *q++=image->colormap[index];
+                    }
+                    break;
+                  }
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+                case 12:
+                  {
+                    const unsigned int
+                      scale_short = 65535U/MAXJ12SAMPLE;
+
+                    for (x=0; x < (long) image->columns; x++)
+                    {
+                      index=(IndexPacket) ScaleQuantumToIndex((ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[x]))));
+                      VerifyColormapIndex(image,index);
+                      indexes[x]=index;
+                      *q++=image->colormap[index];
+                    }
+                    break;
+                  }
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+                default:
+                  {
+                    for (x=0; x < (long) image->columns; x++)
+                      {
+                        index=(IndexPacket) (GETJSAMPLE(jpeg_pixels.t.j[x]));
+                        VerifyColormapIndex(image,index);
+                        indexes[x]=index;
+                        *q++=image->colormap[index];
+                      }
+                  }
                 }
             }
           else
             {
-              if (jpeg_info.data_precision > 8)
+              switch(jpeg_info.data_precision)
                 {
-                  unsigned int
-                    scale_short;
-
-                  scale_short=65535U/MaxValueGivenBits(jpeg_info.data_precision);
-                  for (x=0; x < (long) image->columns; x++)
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+                case 16:
+                  {
+                    /* J16SAMPLE is a 'unsigned short' with maximum value MAXJ16SAMPLE (65535) */
+                    for (x=0; x < (long) image->columns; x++)
                     {
-                      q->red=q->green=q->blue=ScaleShortToQuantum(scale_short*GETJSAMPLE(*p++));
+                      q->red=q->green=q->blue=ScaleShortToQuantum(jpeg_pixels.t.j16[x]);
                       q->opacity=OpaqueOpacity;
                       q++;
                     }
-                }
-              else
-                {
-                  for (x=0; x < (long) image->columns; x++)
+                    break;
+                  }
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+                case 12:
+                  {
+                    /* J12SAMPLE is a 'short' with maximum value MAXJ12SAMPLE (4095) */
+                    const unsigned int
+                      scale_short = 65535U/MAXJ12SAMPLE;
+
+                    for (x=0; x < (long) image->columns; x++)
                     {
-                      q->red=q->green=q->blue=ScaleCharToQuantum(GETJSAMPLE(*p++));
+                      q->red=q->green=q->blue=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[x]));
                       q->opacity=OpaqueOpacity;
                       q++;
                     }
+                    break;
+                  }
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+                default:
+                  {
+                    for (x=0; x < (long) image->columns; x++)
+                      {
+                        q->red=q->green=q->blue=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[x]));
+                        q->opacity=OpaqueOpacity;
+                        q++;
+                      }
+                  }
                 }
             }
         }
       else if ((jpeg_info.output_components == 3) ||
                (jpeg_info.output_components == 4))
         {
-          if (jpeg_info.data_precision > 8)
+          switch(jpeg_info.data_precision)
             {
-              unsigned int
-                scale_short;
-
-              scale_short=65535U/MaxValueGivenBits(jpeg_info.data_precision);
-              for (x=0; x < (long) image->columns; x++)
-                {
-                  q->red=ScaleShortToQuantum(scale_short*GETJSAMPLE(*p++));
-                  q->green=ScaleShortToQuantum(scale_short*GETJSAMPLE(*p++));
-                  q->blue=ScaleShortToQuantum(scale_short*GETJSAMPLE(*p++));
-                  if (jpeg_info.output_components > 3)
-                    q->opacity=ScaleShortToQuantum(scale_short*GETJSAMPLE(*p++));
-                  else
-                    q->opacity=OpaqueOpacity;
-                  q++;
-                }
-            }
-          else
-            {
-              for (x=0; x < (long) image->columns; x++)
-                {
-                  q->red=ScaleCharToQuantum(GETJSAMPLE(*p++));
-                  q->green=ScaleCharToQuantum(GETJSAMPLE(*p++));
-                  q->blue=ScaleCharToQuantum(GETJSAMPLE(*p++));
-                  if (jpeg_info.output_components > 3)
-                    q->opacity=ScaleCharToQuantum(GETJSAMPLE(*p++));
-                  else
-                    q->opacity=OpaqueOpacity;
-                  q++;
-                }
+#if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES
+            case 16:
+              {
+                /* J16SAMPLE is a 'unsigned short' with maximum value MAXJ16SAMPLE (65535) */
+                i = 0;
+                for (x=0; x < (long) image->columns; x++)
+                  {
+                    q->red=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]);
+                    q->green=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]);
+                    q->blue=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]);
+                    if (jpeg_info.output_components > 3)
+                      q->opacity=ScaleShortToQuantum(jpeg_pixels.t.j16[i++]);
+                    else
+                      q->opacity=OpaqueOpacity;
+                    q++;
+                  }
+                break;
+              }
+#endif /* if defined(HAVE_JPEG16_READ_SCANLINES) && HAVE_JPEG16_READ_SCANLINES */
+#if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES
+            case 12:
+              {
+                /* J12SAMPLE is a 'short' with maximum value MAXJ12SAMPLE (4095) */
+                const unsigned int
+                  scale_short = 65535U/MAXJ12SAMPLE;
+
+                i = 0;
+                for (x=0; x < (long) image->columns; x++)
+                  {
+                    q->red=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++]));
+                    q->green=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++]));
+                    q->blue=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++]));
+                    if (jpeg_info.output_components > 3)
+                      q->opacity=ScaleShortToQuantum(scale_short*((unsigned short)jpeg_pixels.t.j12[i++]));
+                    else
+                      q->opacity=OpaqueOpacity;
+                    q++;
+                  }
+                break;
+              }
+#endif /* if defined(HAVE_JPEG12_READ_SCANLINES) && HAVE_JPEG12_READ_SCANLINES */
+            default:
+              {
+                i = 0;
+                for (x=0; x < (long) image->columns; x++)
+                  {
+                    q->red=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++]));
+                    q->green=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++]));
+                    q->blue=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++]));
+                    if (jpeg_info.output_components > 3)
+                      q->opacity=ScaleCharToQuantum(GETJSAMPLE(jpeg_pixels.t.j[i++]));
+                    else
+                      q->opacity=OpaqueOpacity;
+                    q++;
+                  }
+              }
             }
           if (image->colorspace == CMYKColorspace)
             {
@@ -1897,7 +2055,7 @@
         }
     }
   jpeg_destroy_decompress(&jpeg_info);
-  MagickFreeResourceLimitedMemory(jpeg_pixels);
+  MagickFreeResourceLimitedMemory(jpeg_pixels.t.v);
   client_data=FreeMagickClientData(client_data);
   CloseBlob(image);
 
@@ -2500,8 +2658,8 @@
   const ImageAttribute
     *attribute;
 
-  JSAMPLE
-    *jpeg_pixels = (JSAMPLE *) NULL; /* Freed by FreeMagickClientData() */
+  magick_jpeg_pixels_t
+    jpeg_pixels; /* Contents freed by FreeMagickClientData() */
 
   JSAMPROW
     scanline[1];
@@ -2523,9 +2681,11 @@
     *q;
 
   register long
-    i,
     x;
 
+  register unsigned long
+    i;
+
   struct jpeg_error_mgr
     jpeg_error;
 
@@ -2564,6 +2724,7 @@
   if (status == False)
     ThrowJPEGWriterException(FileOpenError,UnableToOpenFile,image);
 
+  (void) memset(&jpeg_pixels,0,sizeof(jpeg_pixels));
   (void) memset(&jpeg_progress,0,sizeof(jpeg_progress));
   (void) memset(&jpeg_info,0,sizeof(jpeg_info));
   (void) memset(&jpeg_error,0,sizeof(jpeg_error));
@@ -2595,7 +2756,7 @@
     }
 
   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-     "  Write JPEG Image: image->orientation = %d",image->orientation);
+                        "  Write JPEG Image: image->orientation = %d",image->orientation);