GraphicsMagick: HEIF: Take row stride into account when reading ...

GraphicsMagick Commits <[email protected]>
Newsgroups gmane.comp.video.graphicsmagick.cvs
Message-ID <mailman.823.1642355894.1940.graphicsmagick-commit@lists.sourceforge.net>
changeset 743f1bcf3052 in /hg/GraphicsMagick
details: http://hg.GraphicsMagick.org/hg/GraphicsMagick?cmd=changeset;node=743f1bcf3052
summary: HEIF: Take row stride into account when reading pixel rows. Add support for 'ping' mode.  Add header magic detection.  Add module aliases.  Add useful traces.

diffstat:

 ChangeLog                              |    6 +
 VisualMagick/installer/inc/version.isx |    4 +-
 coders/heif.c                          |  122 ++++++++++++++++++++++++++++++--
 cscope.files                           |    2 +
 magick/magic.c                         |    4 +
 magick/module_aliases.h                |    3 +-
 magick/version.h                       |    4 +-
 www/Changelog.html                     |    5 +
 8 files changed, 137 insertions(+), 13 deletions(-)

diffs (326 lines):

diff -r 6ca5dbc2fb4f -r 743f1bcf3052 ChangeLog
--- a/ChangeLog	Sat Jan 15 17:09:46 2022 -0600
+++ b/ChangeLog	Sun Jan 16 11:57:59 2022 -0600
@@ -1,3 +1,9 @@
+2022-01-16  Bob Friesenhahn  <[email protected]>
+
+        * coders/heif.c (ReadHEIFImage): Take row stride into account when
+        reading pixel rows. Add support for 'ping' mode.  Add header magic
+        detection.  Add module aliases.  Add useful traces.
+
 2022-01-15  Bob Friesenhahn  <[email protected]>
 
         * coders/jxl.c: Added preliminary JPEG XL support written by
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 VisualMagick/installer/inc/version.isx
--- a/VisualMagick/installer/inc/version.isx	Sat Jan 15 17:09:46 2022 -0600
+++ b/VisualMagick/installer/inc/version.isx	Sun Jan 16 11:57:59 2022 -0600
@@ -10,5 +10,5 @@
 
 #define public MagickPackageName "GraphicsMagick"
 #define public MagickPackageVersion "1.4"
-#define public MagickPackageVersionAddendum ".020220115"
-#define public MagickPackageReleaseDate "snapshot-20220115"
+#define public MagickPackageVersionAddendum ".020220116"
+#define public MagickPackageReleaseDate "snapshot-20220116"
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 coders/heif.c
--- a/coders/heif.c	Sat Jan 15 17:09:46 2022 -0600
+++ b/coders/heif.c	Sun Jan 16 11:57:59 2022 -0600
@@ -34,7 +34,50 @@
 
 #if defined(HasHEIF)
 #include <libheif/heif.h>
+
+/*
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%   I s H E I F                                                               %
+%                                                                             %
+%                                                                             %
+%                                                                             %
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%
+%  Method IsHEIF returns True if the image format type, identified by the
+%  magick string, is supported by this HEIF reader.
+%
+%  The format of the IsHEIF  method is:
+%
+%      unsigned int IsHEIF(const unsigned char *magick,const size_t length)
+%
+%  A description of each parameter follows:
+%
+%    o status:  Method IsHEIF returns True if the image format type is HEIF.
+%
+%    o magick: This string is generally the first few bytes of an image file
+%      or blob.
+%
+%    o length: Specifies the length of the magick string.
+%
+%
+*/
+static unsigned int IsHEIF(const unsigned char *magick,const size_t length)
+{
+  enum heif_filetype_result
+    heif_filetype;
 
+  if (length < 12)
+    return(False);
+
+  heif_filetype = heif_check_filetype(magick, (int) length);
+  if (heif_filetype == heif_filetype_yes_supported)
+    return True;
+
+  return(False);
+}
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
@@ -109,6 +152,11 @@
       unsigned char*
         profile;
 
+      if (image->logging)
+        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                              "Profile \"%s\" with size %" MAGICK_SIZE_T_F "u bytes",
+                              profile_name, (MAGICK_SIZE_T) profile_size);
+
       profile=MagickAllocateResourceLimitedArray(unsigned char*,profile_size,
                                                  sizeof(*profile));
       if (profile == (unsigned char*) NULL)
@@ -121,6 +169,10 @@
 
       if (err.code != heif_error_Ok)
         {
+          if (image->logging)
+            (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                                  "heif_image_handle_get_metadata() reports error \"%s\"",
+                                  err.message);
           MagickFreeResourceLimitedMemory(profile);
           MagickFreeResourceLimitedMemory(ids);
           ThrowReaderException(CorruptImageError,
@@ -210,7 +262,13 @@
       || heif_status.code == heif_error_Unsupported_feature)
     ThrowHEIFReaderException(CoderError, ImageTypeNotSupported, image);
   if (heif_status.code != heif_error_Ok)
-    ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    {
+      if (image->logging)
+        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                              "heif_context_read_from_memory() reports error \"%s\"",
+                              heif_status.message);
+      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    }
 
   /* no support for reading multiple images but could be added */
   if (heif_context_get_number_of_top_level_images(heif) != 1)
@@ -220,30 +278,68 @@
   if (heif_status.code == heif_error_Memory_allocation_error)
     ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
   if (heif_status.code != heif_error_Ok)
-    ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    {
+      if (image->logging)
+        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                              "heif_context_get_primary_image_handle() reports error \"%s\"",
+                              heif_status.message);
+      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    }
 
   image->columns=heif_image_handle_get_width(heif_image_handle);
   image->rows=heif_image_handle_get_height(heif_image_handle);
   if (heif_image_handle_has_alpha_channel(heif_image_handle))
     image->matte=MagickTrue;
 
+  if (image->logging)
+    {
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                            "Geometry: %lux%lu", image->columns, image->rows);
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                            "Matte: %s", image->matte ? "True" : "False");
+    }
+
   if (!ReadMetadata(heif_image_handle, image, exception))
     {
       HEIFReadCleanup();
       return NULL;
     }
 
+  if (image_info->ping)
+    {
+      image->depth = 8;
+      HEIFReadCleanup();
+      CloseBlob(image);
+      return image;
+    }
+
+  if (CheckImagePixelLimits(image, exception) != MagickPass)
+    ThrowHEIFReaderException(ResourceLimitError,ImagePixelLimitExceeded,image);
+
+  /* FIXME: Add decoding options support such as a progress monitor */
   heif_status=heif_decode_image(heif_image_handle, &heif_image,
                                 heif_colorspace_RGB, image->matte ? heif_chroma_interleaved_RGBA :
-                                heif_chroma_interleaved_RGB, NULL);
+                                heif_chroma_interleaved_RGB,
+                                /* const struct heif_decoding_options* */ NULL );
   if (heif_status.code == heif_error_Memory_allocation_error)
     ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
   if (heif_status.code != heif_error_Ok)
-    ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    {
+      if (image->logging)
+        (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                              "heif_decode_image() reports error \"%s\"",
+                              heif_status.message);
+      ThrowHEIFReaderException(CorruptImageError, AnErrorHasOccurredReadingFromFile, image);
+    }
 
   image->depth=heif_image_get_bits_per_pixel(heif_image, heif_channel_interleaved);
   /* the requested channel is interleaved there depth is a sum of all channels
      split it up again: */
+  if (image->logging)
+    {
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                            "heif_image_get_bits_per_pixel: bits_per_pixel=%u", image->depth);
+    }
   if (image->depth == 32 && image->matte)
     image->depth = 8;
   else if (image->depth == 24 && !image->matte)
@@ -255,18 +351,26 @@
   if (!pixels)
     ThrowHEIFReaderException(CoderError, NoDataReturned, image);
 
+  if (image->logging)
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+                          "heif_image_get_plane_readonly: bytes-per-line=%d",
+                          row_stride);
+
+  /* Transfer pixels to image, using row stride to find start of each row. */
   for (y=0; y < (long)image->rows; y++)
     {
+      const uint8_t *line;
       q=SetImagePixelsEx(image,0,y,image->columns,1,exception);
       if (q == (PixelPacket *) NULL)
         ThrowHEIFReaderException(ResourceLimitError,MemoryAllocationFailed,image);
+      line=pixels+y*row_stride;
       for (x=0; x < (long)image->columns; x++)
         {
-          SetRedSample(q,ScaleCharToQuantum(*pixels++));
-          SetGreenSample(q,ScaleCharToQuantum(*pixels++));
-          SetBlueSample(q,ScaleCharToQuantum(*pixels++));
+          SetRedSample(q,ScaleCharToQuantum(*line++));
+          SetGreenSample(q,ScaleCharToQuantum(*line++));
+          SetBlueSample(q,ScaleCharToQuantum(*line++));
           if (image->matte) {
-            SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*pixels++));
+            SetOpacitySample(q,MaxRGB-ScaleCharToQuantum(*line++));
           } else {
             SetOpacitySample(q,OpaqueOpacity);
           }
@@ -332,6 +436,7 @@
   entry=SetMagickInfo("HEIF");
 #if defined(HasHEIF)
   entry->decoder=(DecoderHandler) ReadHEIFImage;
+  entry->magick=(MagickHandler) IsHEIF;
 #endif
   entry->description=description;
   entry->adjoin=False;
@@ -345,6 +450,7 @@
   entry=SetMagickInfo("HEIC");
 #if defined(HasHEIF)
   entry->decoder=(DecoderHandler) ReadHEIFImage;
+  entry->magick=(MagickHandler) IsHEIF;
 #endif
   entry->description=description;
   entry->adjoin=False;
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 cscope.files
--- a/cscope.files	Sat Jan 15 17:09:46 2022 -0600
+++ b/cscope.files	Sun Jan 16 11:57:59 2022 -0600
@@ -27,6 +27,7 @@
 coders/gif.c
 coders/gradient.c
 coders/gray.c
+coders/heif.c
 coders/histogram.c
 coders/hrz.c
 coders/html.c
@@ -35,6 +36,7 @@
 coders/info.c
 coders/jbig.c
 coders/jnx.c
+coders/jpx.c
 coders/jp2.c
 coders/jpeg.c
 coders/label.c
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 magick/magic.c
--- a/magick/magic.c	Sat Jan 15 17:09:46 2022 -0600
+++ b/magick/magic.c	Sun Jan 16 11:57:59 2022 -0600
@@ -60,6 +60,7 @@
 {
 #define MAGIC(name,offset,magic) {name,(const unsigned char * const)magic,sizeof(magic)-1,offset}
   MAGIC("WEBP", 8, "WEBP"),
+  MAGIC("AVIF", 4, "ftypavif"),
   /* MAGIC("AVI", 0, "RIFF"), */
   MAGIC("8BIMWTEXT", 0, "8\000B\000I\000M\000#"),
   MAGIC("8BIMTEXT", 0, "8BIM#"),
@@ -90,6 +91,9 @@
   MAGIC("FITS", 0, "SIMPLE"),
   MAGIC("FPX", 0, "\320\317\021\340"),
   MAGIC("GIF", 0, "GIF8"),
+  MAGIC("HEIC", 4, "ftypheic"),
+  MAGIC("HEIC", 4, "ftypheix"),
+  MAGIC("HEIC", 4, "ftypmif1"),
   MAGIC("HDF", 1, "HDF"),
   MAGIC("HPGL", 0, "IN;"),
   MAGIC("HTML", 1, "HTML"),
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 magick/module_aliases.h
--- a/magick/module_aliases.h	Sat Jan 15 17:09:46 2022 -0600
+++ b/magick/module_aliases.h	Sun Jan 16 11:57:59 2022 -0600
@@ -35,6 +35,7 @@
     MODULEALIAS("APP1","META"),
     MODULEALIAS("APP1JPEG","META"),
     MODULEALIAS("ARW","DCRAW"),
+    MODULEALIAS("AVIF","HEIF"),
     MODULEALIAS("B","GRAY"),
     MODULEALIAS("BIE","JBIG"),
     MODULEALIAS("BIGTIFF","TIFF"),
@@ -72,9 +73,9 @@
     MODULEALIAS("GRAYA","GRAY"),
     MODULEALIAS("GROUP4RAW","TIFF"),
     MODULEALIAS("H","LOGO"),
+    MODULEALIAS("HEIC","HEIF"),
     MODULEALIAS("HTM","HTML"),
     MODULEALIAS("HTTP","URL"),
-    MODULEALIAS("HEIC","HEIF"),
     MODULEALIAS("ICB","TGA"),
     MODULEALIAS("ICC","META"),
     MODULEALIAS("ICM","META"),
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 magick/version.h
--- a/magick/version.h	Sat Jan 15 17:09:46 2022 -0600
+++ b/magick/version.h	Sun Jan 16 11:57:59 2022 -0600
@@ -38,8 +38,8 @@
 #define MagickLibVersion  0x262300
 #define MagickLibVersionText  "1.4"
 #define MagickLibVersionNumber 26,23,0
-#define MagickChangeDate   "20220115"
-#define MagickReleaseDate  "snapshot-20220115"
+#define MagickChangeDate   "20220116"
+#define MagickReleaseDate  "snapshot-20220116"
 
 /*
   The MagickLibInterfaceNewest and MagickLibInterfaceOldest defines
diff -r 6ca5dbc2fb4f -r 743f1bcf3052 www/Changelog.html
--- a/www/Changelog.html	Sat Jan 15 17:09:46 2022 -0600
+++ b/www/Changelog.html	Sun Jan 16 11:57:59 2022 -0600
@@ -35,6 +35,11 @@
 <div class="document">
 
 
+<p>2022-01-16  Bob Friesenhahn  &lt;<a class="reference external" href="mailto:bfriesen&#37;&#52;&#48;simple&#46;dallas&#46;tx&#46;us">bfriesen<span>&#64;</span>simple<span>&#46;</span>dallas<span>&#46;</span>tx<span>&#46;</span>us</a>&gt;</p>
+<blockquote>
+* coders/heif.c (ReadHEIFImage): Take row stride into account when
+reading pixel rows. Add support for 'ping' mode.  Add header magic
+detection.  Add module aliases.  Add useful traces.</blockquote>
 <p>2022-01-15  Bob Friesenhahn  &lt;<a class="reference external" href="mailto:bfriesen&#37;&#52;&#48;simple&#46;dallas&#46;tx&#46;us">bfriesen<span>&#64;</span>simple<span>&#46;</span>dallas<span>&#46;</span>tx<span>&#46;</span>us</a>&gt;</p>
 <blockquote>
 <p>* coders/jxl.c: Added preliminary JPEG XL support written by
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.