[prev in list] [next in list] [prev in thread] [next in thread] 

List:       graphicsmagick-commit
Subject:    [GM-commit] GraphicsMagick: * coders/png.c: Added support for a proposed new...
From:       GraphicsMagick Commits <graphicsmagick-commit () lists ! sourceforge ! net>
Date:       2017-01-26 15:53:52
Message-ID: hg.71bb4d8deb2e.1485446032.2950750188400161634 () src ! simplesystems ! org
[Download RAW message or body]

changeset 71bb4d8deb2e in /hg/GraphicsMagick
details: http://hg.GraphicsMagick.org/hg/GraphicsMagick?cmd=changeset;node=71bb4d8deb2e
summary: * coders/png.c: Added support for a proposed new PNG chunk

diffstat:

 ChangeLog    |   10 +
 coders/png.c |  322 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 324 insertions(+), 8 deletions(-)

diffs (444 lines):

diff -r 5164e2bdd831 -r 71bb4d8deb2e ChangeLog
--- a/ChangeLog	Wed Jan 25 21:03:35 2017 -0600
+++ b/ChangeLog	Thu Jan 26 10:53:44 2017 -0500
@@ -1,3 +1,13 @@
+2017-01-26  Glenn Randers-Pehrson  <glennrp@simple.dallas.tx.us>
+
+	* coders/png.c: Added support for a proposed new PNG chunk
+	(zxIf, read-only) that is currently being discussed on the
+	png-mng-misc at lists.sourceforge.net mailing list.  Enable
+	exIf and zxIf with CPPFLAGS="-DexIf_SUPPORTED -DxzIf_SUPPORTED".
+	If exIf is enabled, only the uncompressed exIF chunk will be
+	written and the hex-encoded zTXt chunk containing the raw Exif
+	profile won't be written.
+
 2017-01-25  Bob Friesenhahn  <bfriesen@simple.dallas.tx.us>
 
 	* coders/msl.c (MSLStartElement): Change test for NULL image
diff -r 5164e2bdd831 -r 71bb4d8deb2e coders/png.c
--- a/coders/png.c	Wed Jan 25 21:03:35 2017 -0600
+++ b/coders/png.c	Thu Jan 26 10:53:44 2017 -0500
@@ -93,6 +93,16 @@
 /*
   Optional declarations. Define or undefine them as you like.
 */
+
+/* After eXIf chunk has been approved:
+#define eXIf_SUPPORTED
+*/
+
+/* Experimental; define one or both of these:
+#define exIf_SUPPORTED
+#define zxIf_SUPPORTED
+*/
+
 /* #define PNG_DEBUG -- turning this on breaks VisualC compiling */
 
 /*
@@ -208,6 +218,10 @@
 static png_byte const mng_sBIT[5]={115,  66,  73,  84, '\0'};
 static png_byte const mng_sRGB[5]={115,  82,  71,  66, '\0'};
 static png_byte const mng_tRNS[5]={116,  82,  78,  83, '\0'};
+#if defined(zxIf_SUPPORTED)
+static png_byte const mng_uxIf[5]={117, 120,  73, 102, '\0'};
+static png_byte const mng_zxIf[5]={122, 120,  73, 102, '\0'};
+#endif
 
 #if defined(JNG_SUPPORTED)
 static png_byte const mng_IDAT[5]={ 73,  68,  65,  84, '\0'};
@@ -1217,6 +1231,154 @@
   return MagickTrue;
 }
 
+#define CHUNK 4096
+#define MAX_CHUNK_SIZE 40000000
+/* Derived from code in libpng-1.4.1beta06 */
+png_size_t
+measure_decompressed_chunk(unsigned char *source, png_size_t chunklength)
+{
+   png_size_t text_size = 0;
+   z_stream zstream;
+   png_byte temp[CHUNK];
+
+   {
+      int ret = Z_OK;
+
+      /* allocate inflate state */
+      zstream.zalloc = Z_NULL;
+      zstream.zfree = Z_NULL;
+      zstream.opaque = Z_NULL;
+      ret = inflateInit(&zstream);
+      if (ret != Z_OK)
+          return (-1);
+
+      zstream.next_in = source;
+      zstream.avail_in = (uInt)(chunklength);
+      zstream.next_out = temp;
+      zstream.avail_out = CHUNK;
+
+      while (zstream.avail_in)
+      {
+         ret = inflate(&zstream, Z_PARTIAL_FLUSH);
+         if (ret != Z_OK && ret != Z_STREAM_END)
+         {
+            inflateReset(&zstream);
+            zstream.avail_in = 0;
+            break;
+         }
+         if (!zstream.avail_out || ret == Z_STREAM_END)
+         {
+            if (text_size == 0)  /* Initialize the decompression buffer */
+            {
+               text_size = CHUNK - zstream.avail_out;
+            }
+            else               /* Enlarge the decompression buffer */
+            {
+              text_size += CHUNK - zstream.avail_out;
+              if (text_size >= PNG_USER_CHUNK_MALLOC_MAX - 1)
+                 return 0;
+            }
+         }
+         if (ret == Z_STREAM_END)
+            break;
+
+         else
+         {
+            zstream.next_out = temp;
+            zstream.avail_out = CHUNK;
+         }
+      }
+
+      inflateReset(&zstream);
+      zstream.avail_in = 0;
+   }
+   return text_size;
+}
+
+/* exif_inf() derived from zlib-1.2.11/examples/zpipe.c/inf()
+   Not copyrighted -- provided to the public domain
+   Version 1.4  11 December 2005  Mark Adler */
+
+#include <assert.h>
+
+/* Decompress from source to dest (copied from zlib-1.2.11/examples).
+   inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
+   allocated for processing, Z_DATA_ERROR if the deflate data is
+   invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
+   the version of the library linked do not match, or Z_ERRNO if there
+   is an error reading or writing the files. */
+
+int exif_inf(png_structp png_ptr, unsigned char *source,
+    unsigned char **dest, size_t n)
+{
+    /* *source: compressed data stream (input)
+       *dest:   inflated data (output)
+       n: length of input
+
+       Returns one of the following:
+       return(-n);  chunk had an error
+       return(n);  success, n is length of inflated data
+     */
+
+    int ret;
+    z_stream strm;
+
+    size_t inflated_length= measure_decompressed_chunk(source, n);
+
+    (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+       "measure_compressed_chunk; inflated_length=%lu\n",inflated_length);
+
+    if (inflated_length == 0)
+        return (-1);
+
+    /* allocate dest */
+#if PNG_LIBPNG_VER >= 14000
+    *dest=(unsigned char *) png_malloc(png_ptr,
+       (png_alloc_size_t) inflated_length);
+#else
+    *dest=(unsigned char *) png_malloc(png_ptr,
+       (png_size_t) inflated_length);
+#endif
+
+    /* allocate inflate state */
+    strm.zalloc = Z_NULL;
+    strm.zfree = Z_NULL;
+    strm.opaque = Z_NULL;
+    strm.avail_in = 0;
+    strm.next_in = Z_NULL;
+    ret = inflateInit(&strm);
+    if (ret != Z_OK)
+        return (-1);
+
+    /* decompress until deflate stream ends or end of file */
+    do {
+
+        strm.avail_in = (int)n;
+        strm.next_in = source;
+
+        /* run inflate() on input until output buffer not full */
+        do {
+            strm.avail_out = (int)inflated_length;
+            strm.next_out = *dest;
+
+            ret = inflate(&strm, Z_NO_FLUSH);
+            assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
+            switch (ret) {
+            case Z_NEED_DICT:
+            case Z_DATA_ERROR:
+            case Z_MEM_ERROR:
+                (void)inflateEnd(&strm);
+                return (-1);
+            }
+        } while (strm.avail_out == 0);
+        /* done when inflate() says it's done */
+    } while (ret != Z_STREAM_END);
+
+    /* clean up and return */
+    (void)inflateEnd(&strm);
+    return ret == Z_STREAM_END ? inflated_length : -1;
+}
+
 #if defined(PNG_UNKNOWN_CHUNKS_SUPPORTED)
 static int read_user_chunk_callback(png_struct *ping, png_unknown_chunkp chunk)
 {
@@ -1230,12 +1392,142 @@
      png_size_t size;
 
      Note that libpng has already taken care of the CRC handling.
+
+     Returns one of the following:
+         return(-n);  chunk had an error
+         return(0);  did not recognize
+         return(n);  success
   */
 
   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
      "    read_user_chunk: found %c%c%c%c chunk",
        chunk->name[0],chunk->name[1],chunk->name[2],chunk->name[3]);
 
+#if defined(zxIf_SUPPORTED)
+  if ((chunk->name[0]  == 122 || chunk->name[0] == 117 ) &&
+      (chunk->name[1] ==   88 || chunk->name[1] == 120 ) &&
+      chunk->name[2] ==   73 &&
+      chunk-> name[3] == 102)
+    {
+      /* process uxIf or zxIf chunk */
+      unsigned char
+        *profile;
+
+      unsigned char
+        *p;
+
+      png_byte
+        *s;
+
+      size_t
+        i;
+
+      LogMagickEvent(CoderEvent,GetMagickModule(),
+        "     recognized uxIf|zxIf chunk");
+
+      image=(Image *) png_get_user_chunk_ptr(ping);
+
+#if PNG_LIBPNG_VER >= 14000
+      profile=(unsigned char *) png_malloc(ping,
+        (png_alloc_size_t) chunk->size+6);
+#else
+      profile=(unsigned char *) png_malloc(ping,
+         (png_size_t) chunk->size+6);
+#endif
+
+      p=profile;
+
+      /* Initialize profile with "Exif\0\0" */
+      *p++ ='E';
+      *p++ ='x';
+      *p++ ='i';
+      *p++ ='f';
+      *p++ ='\0';
+      *p++ ='\0';
+
+      LogMagickEvent(CoderEvent,GetMagickModule(),
+        "     initialized uxIf|zxIf chunk");
+
+      switch (chunk->data[0])
+        {
+          case 'E':
+          case 'I':
+          {
+            /* Uncompressed */
+            /* copy chunk->data to profile */
+            s=chunk->data;
+            for (i=0; i<chunk->size; i++)
+              *p++ = *s++;
+
+            LogMagickEvent(CoderEvent,GetMagickModule(),
+               "     SetImageProfile with %lu bytes",
+               (unsigned long) chunk->size+6);
+
+            (void) SetImageProfile(image,"exif",
+               (const unsigned char *)profile, chunk->size+6);
+
+            return(1);
+          }
+          case '\0':
+          {
+            /* Zlib compressed */
+
+            unsigned char *temp = NULL;
+
+            int inflated_size;
+              
+            png_free(ping,profile);
+
+            s=chunk->data;
+            s++;  // skip compression byte
+
+            /* uncompress chunk->data to temporary profile */
+            inflated_size=exif_inf(ping,s,&temp,chunk->size-1);
+
+            if (inflated_size <= 0)
+            {
+               LogMagickEvent(CoderEvent,GetMagickModule(),
+                  "     inflated_size = %d bytes",inflated_size);
+               return(-1);
+            }
+
+#if PNG_LIBPNG_VER >= 14000
+           profile=(unsigned char *) png_malloc(ping,
+             (png_alloc_size_t) inflated_size+6);
+#else
+           profile=(unsigned char *) png_malloc(ping,
+              (png_size_t) inflated_size+6);
+#endif
+
+           p=profile;
+
+           /* Initialize profile with "Exif\0\0" */
+           *p++ ='E';
+           *p++ ='x';
+           *p++ ='i';
+           *p++ ='f';
+           *p++ ='\0';
+           *p++ ='\0';
+
+           for (i=0; i<inflated_size; i++)
+              *p++=temp[i];
+
+            LogMagickEvent(CoderEvent,GetMagickModule(),
+               "     SetImageProfile with %lu bytes",
+               (unsigned long) inflated_size+6);
+
+            (void) SetImageProfile(image,"exif",
+               (const unsigned char *)profile, inflated_size+6);
+
+            png_free(ping,temp);
+
+            return(1);
+          }
+       }
+    }
+#endif /* zxIf_SUPPORTED */
+
+#if defined(exIf_SUPPORTED)
   if (chunk->name[0]  == 101 &&
       (chunk->name[1] ==  88 || chunk->name[1] == 120 ) &&
       chunk->name[2]  ==  73 &&
@@ -1286,6 +1578,7 @@
          (const unsigned char *)profile, chunk->size+6);
       return(1);
     }
+#endif /* exIf_SUPPORTED */
 
   /* caNv */
   if (chunk->name[0] ==  99 &&
@@ -1312,11 +1605,6 @@
      image->page.y=(size_t) ((chunk->data[12] << 24) |
         (chunk->data[13] << 16) | (chunk->data[14] << 8) | chunk->data[15]);
 
-     /* Return one of the following: */
-        /* return(-n);  chunk had an error */
-        /* return(0);  did not recognize */
-        /* return(n);  success */
-
      return(1);
     }
 
@@ -1528,12 +1816,15 @@
   png_set_benign_errors(ping, 1);
 #endif
 
+  /* Just use libpng's limit (PNG_USER_CHUNK_MALLOC_MAX == 8000000) on
+     chunk size */
 #ifdef PNG_SET_USER_LIMITS_SUPPORTED
   /* Reject images with too many rows or columns */
   png_set_user_limits(ping,
     (png_uint_32) Min(0x7fffffffL, GetMagickResourceLimit(WidthResource)),
     (png_uint_32) Min(0x7fffffffL, GetMagickResourceLimit(HeightResource)));
 #endif /* PNG_SET_USER_LIMITS_SUPPORTED */
+
   (void) LogMagickEvent(CoderEvent,GetMagickModule(),
                             "    PNG width limit: %lu, height limit: %lu",
     (unsigned long) Min(0x7fffffffL, GetMagickResourceLimit(WidthResource)),
@@ -1575,8 +1866,13 @@
 # else
   png_set_keep_unknown_chunks(ping, 1, NULL, 0);
 # endif
-  /* Ignore unused chunks and all unknown chunks except for exIf
-     and caNv */
+  /* Ignore unused chunks and all unknown chunks except for eXIf,
+     zxIf, and caNv */
+#if defined(zxIf_SUPPORTED)
+  png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_uxIf, 1);
+  png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_zxIf, 1);
+#endif
+  png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_eXIf, 1);
   png_set_keep_unknown_chunks(ping, 2, (png_bytep) mng_caNv, 1);
   png_set_keep_unknown_chunks(ping, 1, unused_chunks,
                               (int)sizeof(unused_chunks)/5);
@@ -7348,6 +7644,11 @@
                                       profile_info,
                                       (png_uint_32) profile_length);
               }
+#if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
+            else if (LocaleCompare(profile_name,"exif") == 0)
+              /* Do not write exif; we'll write it later as exIf/zxIf */ 
+              ;
+#endif
             else
               {
                 if (logging)
@@ -7773,8 +8074,8 @@
       png_free(ping,text);
     }
 
+#if defined(exIf_SUPPORTED) || defined(zxIf_SUPPORTED)
   /* write exIf profile */
-
   {
     ImageProfileIterator
       *profile_iterator;
@@ -7810,11 +8111,15 @@
 
                 length=(png_uint_32) profile_length;
 
+#if defined(zxIf_SUPPORTED)
+      /* For now, write uncompressed exIf/eXIf chunk */
+#endif
 #if 0 /* eXIf chunk is registered */
                 PNGType(chunk,mng_eXIf);
 #else /* eXIf chunk not yet registered; write exIf instead */
                 PNGType(chunk,mng_exIf);
 #endif
+
                 if (length < 7)
                   break;  /* othewise crashes */
 
@@ -7833,6 +8138,7 @@
         DeallocateImageProfileIterator(profile_iterator);
       }
     }
+#endif /* exIf_SUPPORTED) || zxIf_SUPPORTED */
 
   if (logging)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, SlashDot.org! http://sdm.link/slashdot
_______________________________________________
Graphicsmagick-commit mailing list
Graphicsmagick-commit@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/graphicsmagick-commit
[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic