]> granicus.if.org Git - imagemagick/blobdiff - coders/png.c
(no commit message)
[imagemagick] / coders / png.c
index ea81bdb1f2d1fa1724835330d8f546c2134ea6c6..847af83c1b9d5d389e66fef60bf5d4d2d3b91ef0 100644 (file)
@@ -18,7 +18,7 @@
 %                               November 1997                                 %
 %                                                                             %
 %                                                                             %
-%  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  %
         }
 
 #define LBR01PixelRed(pixel) \
-        (ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
-        0 : QuantumRange);
+        (SetPixelRed(image, \
+        ScaleQuantumToChar(GetPixelRed(image,(pixel))) < 0x10 ? \
+        0 : QuantumRange,(pixel)));
 
 #define LBR01PixelGreen(pixel) \
-        (ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
-        0 : QuantumRange);
+        (SetPixelGreen(image, \
+        ScaleQuantumToChar(GetPixelGreen(image,(pixel))) < 0x10 ? \
+        0 : QuantumRange,(pixel)));
 
 #define LBR01PixelBlue(pixel) \
-        (ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
-        0 : QuantumRange);
+        (SetPixelBlue(image, \
+        ScaleQuantumToChar(GetPixelBlue(image,(pixel))) < 0x10 ? \
+        0 : QuantumRange,(pixel)));
 
 #define LBR01PixelAlpha(pixel) \
-        (ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
-        0 : QuantumRange);
+        (SetPixelAlpha(image, \
+        ScaleQuantumToChar(GetPixelAlpha(image,(pixel))) < 0x10 ? \
+        0 : QuantumRange,(pixel)));
 
 #define LBR01PixelRGB(pixel) \
         { \
         }
 
 
+#if MAGICKCORE_QUANTUM_DEPTH > 8
 /* LBR08: Replicate top 8 bits */
 
 #define LBR08PacketRed(pixelpacket) \
         LBR08PixelRGB((pixel)); \
         LBR08PixelAlpha((pixel)); \
         }
+#endif /* MAGICKCORE_QUANTUM_DEPTH > 8 */
 
 
+#if MAGICKCORE_QUANTUM_DEPTH > 16
 /* LBR16: Replicate top 16 bits */
 
 #define LBR16PacketRed(pixelpacket) \
         LBR16PixelRGB((pixel)); \
         LBR16PixelAlpha((pixel)); \
         }
+#endif /* MAGICKCORE_QUANTUM_DEPTH > 16 */
 
 /*
   Establish thread safety.
@@ -872,7 +880,9 @@ typedef struct _MngInfo
     write_png_compression_filter,
     write_png8,
     write_png24,
-    write_png32;
+    write_png32,
+    write_png48,
+    write_png64;
 
 #ifdef MNG_BASI_SUPPORTED
   size_t
@@ -2025,6 +2035,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
     pass,
     ping_bit_depth,
     ping_color_type,
+    ping_file_depth,
     ping_interlace_method,
     ping_compression_method,
     ping_filter_method,
@@ -2074,9 +2085,6 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
   QuantumInfo
     *quantum_info;
 
-  unsigned char
-    *ping_pixels;
-
   ssize_t
     ping_rowbytes,
     y;
@@ -2098,6 +2106,9 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
   ssize_t
     j;
 
+  unsigned char
+    *volatile ping_pixels;
+
 #ifdef PNG_UNKNOWN_CHUNKS_SUPPORTED
   png_byte unused_chunks[]=
   {
@@ -2278,6 +2289,15 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
   png_set_read_user_chunk_fn(ping, image, read_vpag_chunk_callback);
 #endif
 
+#ifdef PNG_SET_USER_LIMITS_SUPPORTED
+#  if (PNG_LIBPNG_VER >= 10400)
+    /* Limit the size of the chunk storage cache used for sPLT, text,
+     * and unknown chunks.
+     */
+    png_set_chunk_cache_max(ping, 32767);
+#  endif
+#endif
+
 #ifdef PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
     /* Disable new libpng-1.5.10 feature */
     png_set_check_for_invalid_index (ping, 0);
@@ -2309,6 +2329,20 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
                &ping_interlace_method,&ping_compression_method,
                &ping_filter_method);
 
+  ping_file_depth = ping_bit_depth;
+
+  /* Save bit-depth and color-type in case we later want to write a PNG00 */
+  {
+      char
+        msg[MaxTextExtent];
+
+      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_color_type);
+      (void) SetImageProperty(image,"png:IHDR.color-type-orig ",msg,exception);
+
+      (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
+      (void) SetImageProperty(image,"png:IHDR.bit-depth-orig  ",msg,exception);
+  }
+
   (void) png_get_tRNS(ping, ping_info, &ping_trans_alpha, &ping_num_trans,
                       &ping_trans_color);
 
@@ -2316,11 +2350,8 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
 
   if (ping_bit_depth < 8)
     {
-      if (((int) ping_color_type == PNG_COLOR_TYPE_PALETTE))
-        {
-          png_set_packing(ping);
-          ping_bit_depth = 8;
-        }
+       png_set_packing(ping);
+       ping_bit_depth = 8;
     }
 
   image->depth=ping_bit_depth;
@@ -2494,18 +2525,23 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
         &image->chromaticity.blue_primary.x,
         &image->chromaticity.blue_primary.y);
 
+      ping_found_cHRM=MagickTrue;
     }
 
   if (image->rendering_intent != UndefinedIntent)
     {
-      png_set_sRGB(ping,ping_info,
-         Magick_RenderingIntent_to_PNG_RenderingIntent
-         (image->rendering_intent));
-      png_set_gAMA(ping,ping_info,1.000f/2.200f);
-      png_set_cHRM(ping,ping_info,
-                  0.6400f, 0.3300f, 0.3000f, 0.6000f,
-                  0.1500f, 0.0600f, 0.3127f, 0.3290f);
+      if (ping_found_sRGB != MagickTrue)
+      {
+         png_set_sRGB(ping,ping_info,
+            Magick_RenderingIntent_to_PNG_RenderingIntent
+            (image->rendering_intent));
+         png_set_gAMA(ping,ping_info,1.000f/2.200f);
+         file_gamma=1.000f/2.200f;
+         ping_found_sRGB=MagickTrue;
+         ping_found_cHRM=MagickTrue;
+      }
     }
+
 #if defined(PNG_oFFs_SUPPORTED)
   if (png_get_valid(ping,ping_info,PNG_INFO_oFFs))
     {
@@ -2648,16 +2684,16 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
 
         bkgd_scale = 1;
 
-        if (ping_bit_depth == 1)
+        if (ping_file_depth == 1)
            bkgd_scale = 255;
 
-        else if (ping_bit_depth == 2)
+        else if (ping_file_depth == 2)
            bkgd_scale = 85;
 
-        else if (ping_bit_depth == 4)
+        else if (ping_file_depth == 4)
            bkgd_scale = 17;
 
-        if (ping_bit_depth <= 8)
+        if (ping_file_depth <= 8)
            bkgd_scale *= 257;
 
         ping_background->red *= bkgd_scale;
@@ -2709,7 +2745,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "    Reading PNG tRNS chunk.");
 
-      max_sample = (int) ((one << ping_bit_depth) - 1);
+      max_sample = (int) ((one << ping_file_depth) - 1);
 
       if ((ping_color_type == PNG_COLOR_TYPE_GRAY &&
           (int)ping_trans_color->gray > max_sample) ||
@@ -2730,7 +2766,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
           int
              scale_to_short;
 
-          scale_to_short = 65535L/((1UL << ping_bit_depth)-1);
+          scale_to_short = 65535L/((1UL << ping_file_depth)-1);
 
           /* Scale transparent_color to short */
           transparent_color.red= scale_to_short*ping_trans_color->red;
@@ -2796,8 +2832,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
     {
       if ((!png_get_valid(ping,ping_info,PNG_INFO_gAMA) ||
           image->gamma == 1.0) &&
-          !png_get_valid(ping,ping_info,PNG_INFO_cHRM) &&
-          !png_get_valid(ping,ping_info,PNG_INFO_sRGB))
+          ping_found_cHRM != MagickTrue && ping_found_sRGB != MagickTrue)
         {
           /* Set image->gamma to 1.0, image->rendering_intent to Undefined,
            * image->colorspace to GRAY, and reset image->chromaticity.
@@ -2818,7 +2853,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
 
       image->storage_class=PseudoClass;
       one=1;
-      image->colors=one << ping_bit_depth;
+      image->colors=one << ping_file_depth;
 #if (MAGICKCORE_QUANTUM_DEPTH == 8)
       if (image->colors > 256)
         image->colors=256;
@@ -2875,7 +2910,7 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
           size_t
             scale;
 
-          scale=(QuantumRange/((1UL << ping_bit_depth)-1));
+          scale=(QuantumRange/((1UL << ping_file_depth)-1));
 
           if (scale < 1)
              scale=1;
@@ -2894,14 +2929,14 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
       char
         msg[MaxTextExtent];
 
-     /* encode ping_width, ping_height, ping_bit_depth, ping_color_type,
+     /* encode ping_width, ping_height, ping_file_depth, ping_color_type,
         ping_interlace_method in value */
 
      (void) FormatLocaleString(msg,MaxTextExtent,
          "%d, %d",(int) ping_width, (int) ping_height);
      (void) SetImageProperty(image,"png:IHDR.width,height    ",msg,exception);
 
-     (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_bit_depth);
+     (void) FormatLocaleString(msg,MaxTextExtent,"%d",(int) ping_file_depth);
      (void) SetImageProperty(image,"png:IHDR.bit_depth       ",msg,exception);
 
      (void) FormatLocaleString(msg,MaxTextExtent,"%d (%s)",
@@ -2992,6 +3027,8 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
   if (quantum_info == (QuantumInfo *) NULL)
      png_error(ping,"Failed to allocate quantum_info");
 
+  (void) SetQuantumEndian(image,quantum_info,MSBEndian);
+
   {
 
    MagickBooleanType
@@ -3020,6 +3057,10 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
             row_offset=0;
 
           png_read_row(ping,ping_pixels+row_offset,NULL);
+
+          if (pass < num_passes-1)
+            continue;
+
           q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
 
           if (q == (Quantum *) NULL)
@@ -3134,6 +3175,9 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
 
       for (y=0; y < (ssize_t) image->rows; y++)
       {
+        Quantum
+           alpha;
+
         if (num_passes > 1)
           row_offset=ping_rowbytes*y;
 
@@ -3141,7 +3185,11 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
           row_offset=0;
 
         png_read_row(ping,ping_pixels+row_offset,NULL);
-        q=GetAuthenticPixels(image,0,y,image->columns,1,exception);
+
+        if (pass < num_passes-1)
+          continue;
+
+        q=QueueAuthenticPixels(image,0,y,image->columns,1,exception);
 
         if (q == (Quantum *) NULL)
           break;
@@ -3151,69 +3199,21 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
 
         switch (ping_bit_depth)
         {
-          case 1:
-          {
-            register ssize_t
-              bit;
-
-            for (x=(ssize_t) image->columns-7; x > 0; x-=8)
-            {
-              for (bit=7; bit >= 0; bit--)
-                *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
-              p++;
-            }
-
-            if ((image->columns % 8) != 0)
-              {
-                for (bit=7; bit >= (ssize_t) (8-(image->columns % 8)); bit--)
-                  *r++=(Quantum) ((*p) & (0x01 << bit) ? 0x01 : 0x00);
-              }
-
-            break;
-          }
-
-          case 2:
-          {
-            for (x=(ssize_t) image->columns-3; x > 0; x-=4)
-            {
-              *r++=(*p >> 6) & 0x03;
-              *r++=(*p >> 4) & 0x03;
-              *r++=(*p >> 2) & 0x03;
-              *r++=(*p++) & 0x03;
-            }
-
-            if ((image->columns % 4) != 0)
-              {
-                for (i=3; i >= (ssize_t) (4-(image->columns % 4)); i--)
-                  *r++=(Quantum) ((*p >> (i*2)) & 0x03);
-              }
-
-            break;
-          }
-
-          case 4:
-          {
-            for (x=(ssize_t) image->columns-1; x > 0; x-=2)
-            {
-              *r++=(*p >> 4) & 0x0f;
-              *r++=(*p++) & 0x0f;
-            }
-
-            if ((image->columns % 2) != 0)
-              *r++=(*p++ >> 4) & 0x0f;
-
-            break;
-          }
-
           case 8:
           {
+
             if (ping_color_type == 4)
               for (x=(ssize_t) image->columns-1; x >= 0; x--)
               {
                 *r++=*p++;
-                SetPixelAlpha(image,ScaleCharToQuantum((unsigned char) *p++),q);
-                if (GetPixelAlpha(image,q) != OpaqueAlpha)
+
+                alpha=ScaleCharToQuantum((unsigned char)*p++);
+
+                SetPixelAlpha(image,alpha,q);
+
+                if (alpha != OpaqueAlpha)
                   found_transparent_pixel = MagickTrue;
+
                 q+=GetPixelChannels(image);
               }
 
@@ -3250,9 +3250,13 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
                     quantum=0;
 
                   quantum|=(*p++);
-                  SetPixelAlpha(image,ScaleShortToQuantum(quantum),q);
-                  if (GetPixelAlpha(image,q) != OpaqueAlpha)
+
+                  alpha=ScaleShortToQuantum(quantum);
+                  SetPixelAlpha(image,alpha,q);
+
+                  if (alpha != OpaqueAlpha)
                     found_transparent_pixel = MagickTrue;
+
                   q+=GetPixelChannels(image);
                 }
 
@@ -3263,8 +3267,10 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
               if (ping_color_type == 4)
                 {
                   SetPixelAlpha(image,*p++,q);
+
                   if (GetPixelAlpha(image,q) != OpaqueAlpha)
                     found_transparent_pixel = MagickTrue;
+
                   p++;
                   q+=GetPixelChannels(image);
                 }
@@ -3609,6 +3615,32 @@ static Image *ReadOnePNGImage(MngInfo *mng_info,
         (png_get_valid(ping,ping_info,PNG_INFO_tRNS))) ?
         BlendPixelTrait : UndefinedPixelTrait;
 
+#if 0  /* I'm not sure what's wrong here but it does not work. */
+    if (image->alpha_trait == BlendPixelTrait)
+    {
+      if (ping_color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
+        (void) SetImageType(image,GrayscaleMatteType,exception);
+
+      else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
+        (void) SetImageType(image,PaletteMatteType,exception);
+
+      else
+        (void) SetImageType(image,TrueColorMatteType,exception);
+    }
+
+    else
+    {
+      if (ping_color_type == PNG_COLOR_TYPE_GRAY)
+        (void) SetImageType(image,GrayscaleType,exception);
+
+      else if (ping_color_type == PNG_COLOR_TYPE_PALETTE)
+        (void) SetImageType(image,PaletteType,exception);
+
+      else
+        (void) SetImageType(image,TrueColorType,exception);
+    }
+#endif
+
    /* Set more properties for identify to retrieve */
    {
      char
@@ -3835,18 +3867,9 @@ static Image *ReadPNGImage(const ImageInfo *image_info,ExceptionInfo *exception)
     }
 
   if ((IssRGBColorspace(image->colorspace) != MagickFalse) &&
-      (image->gamma == 1.0))
+      ((image->gamma < .45) || (image->gamma > .46)))
     SetImageColorspace(image,RGBColorspace,exception);
 
-  if (LocaleCompare(image_info->magick,"PNG24") == 0)
-    {
-      (void) SetImageType(image,TrueColorType,exception);
-      image->alpha_trait=UndefinedPixelTrait;
-    }
-
-  if (LocaleCompare(image_info->magick,"PNG32") == 0)
-    (void) SetImageType(image,TrueColorMatteType,exception);
-
   if (logging != MagickFalse)
     (void) LogMagickEvent(CoderEvent,GetMagickModule(),
         "  page.w: %.20g, page.h: %.20g,page.x: %.20g, page.y: %.20g.",
@@ -7281,7 +7304,7 @@ ModuleExport size_t RegisterPNGImage(void)
 
   entry->magick=(IsImageFormatHandler *) IsPNG;
   entry->adjoin=MagickFalse;
-  entry->description=ConstantString("opaque 24-bit RGB");
+  entry->description=ConstantString("opaque or binary transparent 24-bit RGB");
   entry->module=ConstantString("PNG");
   (void) RegisterMagickInfo(entry);
 
@@ -7298,6 +7321,45 @@ ModuleExport size_t RegisterPNGImage(void)
   entry->module=ConstantString("PNG");
   (void) RegisterMagickInfo(entry);
 
+  entry=SetMagickInfo("PNG48");
+
+#if defined(MAGICKCORE_PNG_DELEGATE)
+  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
+  entry->encoder=(EncodeImageHandler *) WritePNGImage;
+#endif
+
+  entry->magick=(IsImageFormatHandler *) IsPNG;
+  entry->adjoin=MagickFalse;
+  entry->description=ConstantString("opaque or binary transparent 48-bit RGB");
+  entry->module=ConstantString("PNG");
+  (void) RegisterMagickInfo(entry);
+
+  entry=SetMagickInfo("PNG64");
+
+#if defined(MAGICKCORE_PNG_DELEGATE)
+  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
+  entry->encoder=(EncodeImageHandler *) WritePNGImage;
+#endif
+
+  entry->magick=(IsImageFormatHandler *) IsPNG;
+  entry->adjoin=MagickFalse;
+  entry->description=ConstantString("opaque or transparent 64-bit RGBA");
+  entry->module=ConstantString("PNG");
+  (void) RegisterMagickInfo(entry);
+
+  entry=SetMagickInfo("PNG00");
+
+#if defined(MAGICKCORE_PNG_DELEGATE)
+  entry->decoder=(DecodeImageHandler *) ReadPNGImage;
+  entry->encoder=(EncodeImageHandler *) WritePNGImage;
+#endif
+
+  entry->magick=(IsImageFormatHandler *) IsPNG;
+  entry->adjoin=MagickFalse;
+  entry->description=ConstantString("PNG inheriting subformat from original");
+  entry->module=ConstantString("PNG");
+  (void) RegisterMagickInfo(entry);
+
   entry=SetMagickInfo("JNG");
 
 #if defined(JNG_SUPPORTED)
@@ -7347,6 +7409,9 @@ ModuleExport void UnregisterPNGImage(void)
   (void) UnregisterMagickInfo("PNG8");
   (void) UnregisterMagickInfo("PNG24");
   (void) UnregisterMagickInfo("PNG32");
+  (void) UnregisterMagickInfo("PNG48");
+  (void) UnregisterMagickInfo("PNG64");
+  (void) UnregisterMagickInfo("PNG00");
   (void) UnregisterMagickInfo("JNG");
 
 #ifdef PNG_SETJMP_NOT_THREAD_SAFE
@@ -7669,7 +7734,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
     x;
 
   unsigned char
-    *ping_pixels;
+    *volatile ping_pixels;
 
   volatile int
     image_colors,
@@ -7857,8 +7922,9 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
 
   if (image->storage_class == PseudoClass &&
      (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32 ||
-     (mng_info->write_png_colortype != 0 &&
-     mng_info->write_png_colortype != 4)))
+     mng_info->write_png48 || mng_info->write_png64 ||
+     (mng_info->write_png_colortype != 1 &&
+     mng_info->write_png_colortype != 5)))
     {
       (void) SyncImage(image,exception);
       image->storage_class = DirectClass;
@@ -8087,7 +8153,41 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
       image->depth = 8;
 #endif
 
-  /* Normally we run this just once, but in the case of writing PNG8
+  if (image->storage_class != PseudoClass && mng_info->write_png_colortype &&
+     (mng_info->write_png_colortype > 4 || (mng_info->write_png_depth >= 8 &&
+     mng_info->write_png_colortype < 4 &&
+     image->alpha_trait != BlendPixelTrait)))
+  {
+     /* Avoid the expensive BUILD_PALETTE operation if we're sure that we
+      * are not going to need the result.
+      */
+     image_colors = (int) image->colors;
+     number_opaque = (int) image->colors;
+     if (mng_info->write_png_colortype == 1 ||
+        mng_info->write_png_colortype == 5)
+       ping_have_color=MagickFalse;
+     else
+       ping_have_color=MagickTrue;
+     ping_have_non_bw=MagickFalse;
+
+     if (image->alpha_trait == BlendPixelTrait)
+       {
+         number_transparent = 2;
+         number_semitransparent = 1;
+       }
+
+     else
+       {
+         number_transparent = 0;
+         number_semitransparent = 0;
+       }
+  }
+
+  else
+  {
+  /* BUILD_PALETTE
+   *
+   * Normally we run this just once, but in the case of writing PNG8
    * we reduce the transparency to binary and run again, then if there
    * are still too many colors we reduce to a simple 4-4-4-1, then 3-3-3-1
    * RGBA palette and run again, and then to a simple 3-3-2-1 RGBA
@@ -8102,8 +8202,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
 
   for (j=0; j<6; j++)
   {
-    /* BUILD_PALETTE
-     *
+    /*
      * Sometimes we get DirectClass images that have 256 colors or fewer.
      * This code will build a colormap.
      *
@@ -8367,13 +8466,8 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
          ping_have_color=MagickFalse;
          ping_have_non_bw=MagickFalse;
 
-         if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
-         {
-           ping_have_color=MagickTrue;
-           ping_have_non_bw=MagickFalse;
-         }
-
-         if (IssRGBColorspace(image->colorspace) != MagickFalse)
+         if ((IssRGBCompatibleColorspace(image->colorspace) == MagickFalse) ||
+             (IssRGBColorspace(image->colorspace) != MagickFalse))
          {
            ping_have_color=MagickTrue;
            ping_have_non_bw=MagickTrue;
@@ -8880,6 +8974,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
       }
     }
   }
+  }
   /* END OF BUILD_PALETTE */
 
   /* If we are excluding the tRNS chunk and there is transparency,
@@ -9100,6 +9195,9 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
   if (mng_info->write_png8 || mng_info->write_png24 || mng_info->write_png32)
      image_depth=8;
 
+  if (mng_info->write_png48 || mng_info->write_png64)
+     image_depth=16;
+
   if (mng_info->write_png_depth != 0)
      image_depth=mng_info->write_png_depth;
 
@@ -9310,13 +9408,21 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
       }
     } /* end of write_png8 */
 
-  else if (mng_info->write_png24 || mng_info->write_png_colortype == 3)
+  else if (mng_info->write_png_colortype == 1)
+    {
+      image_matte=MagickFalse;
+      ping_color_type=(png_byte) PNG_COLOR_TYPE_GRAY;
+    }
+
+  else if (mng_info->write_png24 || mng_info->write_png48 ||
+      mng_info->write_png_colortype == 3)
     {
       image_matte=MagickFalse;
       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB;
     }
 
-  else if (mng_info->write_png32 || mng_info->write_png_colortype == 7)
+  else if (mng_info->write_png32 || mng_info->write_png64 ||
+      mng_info->write_png_colortype == 7)
     {
       image_matte=MagickTrue;
       ping_color_type=(png_byte) PNG_COLOR_TYPE_RGB_ALPHA;
@@ -10473,9 +10579,11 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
     png_error(ping,"Memory allocation for quantum_info failed");
   quantum_info->format=UndefinedQuantumFormat;
   quantum_info->depth=image_depth;
+  (void) SetQuantumEndian(image,quantum_info,MSBEndian);
   num_passes=png_set_interlace_handling(ping);
 
   if ((!mng_info->write_png8 && !mng_info->write_png24 &&
+       !mng_info->write_png48 && !mng_info->write_png64 &&
        !mng_info->write_png32) &&
        (mng_info->IsPalette ||
        (image_info->type == BilevelType)) &&
@@ -10547,10 +10655,10 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
   else   /* Not Palette, Bilevel, or Opaque Monochrome */
     {
       if ((!mng_info->write_png8 && !mng_info->write_png24 &&
-         !mng_info->write_png32) &&
-         (image_matte != MagickFalse ||
-         (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
-         (mng_info->IsPalette) && ping_have_color == MagickFalse)
+          !mng_info->write_png48 && !mng_info->write_png64 &&
+          !mng_info->write_png32) && (image_matte != MagickFalse ||
+          (ping_bit_depth >= MAGICKCORE_QUANTUM_DEPTH)) &&
+          (mng_info->IsPalette) && ping_have_color == MagickFalse)
         {
           register const Quantum
             *p;
@@ -10613,9 +10721,12 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
 
           for (pass=0; pass < num_passes; pass++)
           {
-            if ((image_depth > 8) || (mng_info->write_png24 ||
+            if ((image_depth > 8) ||
+                mng_info->write_png24 ||
                 mng_info->write_png32 ||
-                (!mng_info->write_png8 && !mng_info->IsPalette)))
+                mng_info->write_png48 ||
+                mng_info->write_png64 ||
+                (!mng_info->write_png8 && !mng_info->IsPalette))
             {
               for (y=0; y < (ssize_t) image->rows; y++)
               {
@@ -10663,9 +10774,11 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
             }
 
           else
-            /* not ((image_depth > 8) || (mng_info->write_png24 ||
-                mng_info->write_png32 ||
-                (!mng_info->write_png8 && !mng_info->IsPalette))) */
+            /* not ((image_depth > 8) ||
+                mng_info->write_png24 || mng_info->write_png32 ||
+                mng_info->write_png48 || mng_info->write_png64 ||
+                (!mng_info->write_png8 && !mng_info->IsPalette))
+             */
             {
               if ((ping_color_type != PNG_COLOR_TYPE_GRAY) &&
                   (ping_color_type != PNG_COLOR_TYPE_GRAY_ALPHA))
@@ -10831,7 +10944,7 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
                   "  Setting up text chunk");
 
                 (void) LogMagickEvent(CoderEvent,GetMagickModule(),
-                  "    keyword: %s",text[0].key);
+                  "    keyword: '%s'",text[0].key);
               }
 
             png_set_text(ping,ping_info,text,1);
@@ -11002,6 +11115,23 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
 %               The only loss in data is the reduction of the sample depth
 %               to 8.
 %
+%    o PNG48:   A 16-bit per sample RGB PNG datastream is written.  The tRNS
+%               chunk can be present to convey binary transparency by naming
+%               one of the colors as transparent.  If the image has more
+%               than one transparent color, has semitransparent pixels, or
+%               has an opaque pixel with the same RGB components as the
+%               transparent color, an image is not written.
+%
+%    o PNG64:   A 16-bit per sample RGBA PNG is written.  Partial
+%               transparency is permitted, i.e., the alpha sample for
+%               each pixel can have any value from 0 to 65535. The alpha
+%               channel is present even if the image is fully opaque.
+%
+%    o PNG00:   A PNG that inherits its colortype and bit-depth from the input
+%               image, if the input was a PNG, is written.  If these values
+%               cannot be found, then "PNG00" falls back to the regular "PNG"
+%               format.
+%
 %    o -define: For more precise control of the PNG output, you can use the
 %               Image options "png:bit-depth" and "png:color-type".  These
 %               can be set from the commandline with "-define" and also
@@ -11025,9 +11155,10 @@ static MagickBooleanType WriteOnePNGImage(MngInfo *mng_info,
 %               When png:color-type is 4 (Gray-Matte) or 6 (RGB-Matte),
 %               png:bit-depth can be 8 or 16.
 %
-%  If the image cannot be written without loss with the requested bit-depth
-%  and color-type, a PNG file will not be written, and the encoder will
-%  return MagickFalse.
+%               If the image cannot be written without loss with the
+%               requested bit-depth and color-type, a PNG file will not
+%               be written, a warning will be issued, and the encoder will
+%               return MagickFalse.
 %
 %  Since image encoders should not be responsible for the "heavy lifting",
 %  the user should make sure that ImageMagick has already reduced the
@@ -11126,32 +11257,91 @@ static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
   mng_info->write_png8=LocaleCompare(image_info->magick,"PNG8") == 0;
   mng_info->write_png24=LocaleCompare(image_info->magick,"PNG24") == 0;
   mng_info->write_png32=LocaleCompare(image_info->magick,"PNG32") == 0;
+  mng_info->write_png48=LocaleCompare(image_info->magick,"PNG48") == 0;
+  mng_info->write_png64=LocaleCompare(image_info->magick,"PNG64") == 0;
 
   value=GetImageOption(image_info,"png:format");
 
   if (value != (char *) NULL)
     {
+      mng_info->write_png8 = MagickFalse;
+      mng_info->write_png24 = MagickFalse;
+      mng_info->write_png32 = MagickFalse;
+      mng_info->write_png48 = MagickFalse;
+      mng_info->write_png64 = MagickFalse;
+
       if (LocaleCompare(value,"png8") == 0)
-        {
         mng_info->write_png8 = MagickTrue;
-        mng_info->write_png24 = MagickFalse;
-        mng_info->write_png32 = MagickFalse;
-        }
 
       else if (LocaleCompare(value,"png24") == 0)
-        {
-        mng_info->write_png8 = MagickFalse;
         mng_info->write_png24 = MagickTrue;
-        mng_info->write_png32 = MagickFalse;
-        }
 
       else if (LocaleCompare(value,"png32") == 0)
-        {
-        mng_info->write_png8 = MagickFalse;
-        mng_info->write_png24 = MagickFalse;
         mng_info->write_png32 = MagickTrue;
+
+      else if (LocaleCompare(value,"png48") == 0)
+        mng_info->write_png48 = MagickTrue;
+
+      else if (LocaleCompare(value,"png64") == 0)
+        mng_info->write_png64 = MagickTrue;
+    }
+
+  if (LocaleCompare(value,"png00") == 0)
+    {
+      /* Retrieve png:IHDR.bit-depth-orig and png:IHDR.color-type-orig
+         Note that whitespace at the end of the property names must match
+         that in the corresponding SetImageProperty() calls.
+       */
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+         "  Format=%s",value);
+
+      value=GetImageProperty(image,"png:IHDR.bit-depth-orig  ",exception);
+
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+         "  png00 inherited bit depth=%s",value);
+
+      if (value != (char *) NULL)
+        {
+          if (LocaleCompare(value,"1") == 0)
+            mng_info->write_png_depth = 1;
+
+          else if (LocaleCompare(value,"1") == 0)
+            mng_info->write_png_depth = 2;
+
+          else if (LocaleCompare(value,"2") == 0)
+            mng_info->write_png_depth = 4;
+
+          else if (LocaleCompare(value,"8") == 0)
+            mng_info->write_png_depth = 8;
+
+          else if (LocaleCompare(value,"16") == 0)
+            mng_info->write_png_depth = 16;
+        }
+
+      value=GetImageProperty(image,"png:IHDR.color-type-orig ",exception);
+
+      (void) LogMagickEvent(CoderEvent,GetMagickModule(),
+         "  png00 inherited color type=%s",value);
+
+      if (value != (char *) NULL)
+        {
+          if (LocaleCompare(value,"0") == 0)
+            mng_info->write_png_colortype = 1;
+
+          else if (LocaleCompare(value,"2") == 0)
+            mng_info->write_png_colortype = 3;
+
+          else if (LocaleCompare(value,"3") == 0)
+            mng_info->write_png_colortype = 4;
+
+          else if (LocaleCompare(value,"4") == 0)
+            mng_info->write_png_colortype = 5;
+
+          else if (LocaleCompare(value,"6") == 0)
+            mng_info->write_png_colortype = 7;
         }
     }
+
   if (mng_info->write_png8)
     {
       mng_info->write_png_colortype = /* 3 */ 4;
@@ -11189,6 +11379,36 @@ static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
       (void) SyncImage(image,exception);
     }
 
+  if (mng_info->write_png48)
+    {
+      mng_info->write_png_colortype = /* 2 */ 3;
+      mng_info->write_png_depth = 16;
+      image->depth = 16;
+
+      if (image->alpha_trait == BlendPixelTrait)
+        (void) SetImageType(image,TrueColorMatteType,exception);
+
+      else
+        (void) SetImageType(image,TrueColorType,exception);
+
+      (void) SyncImage(image,exception);
+    }
+
+  if (mng_info->write_png64)
+    {
+      mng_info->write_png_colortype = /* 6 */  7;
+      mng_info->write_png_depth = 16;
+      image->depth = 16;
+
+      if (image->alpha_trait == BlendPixelTrait)
+        (void) SetImageType(image,TrueColorMatteType,exception);
+
+      else
+        (void) SetImageType(image,TrueColorType,exception);
+
+      (void) SyncImage(image,exception);
+    }
+
   value=GetImageOption(image_info,"png:bit-depth");
 
   if (value != (char *) NULL)
@@ -11415,7 +11635,7 @@ static MagickBooleanType WritePNGImage(const ImageInfo *image_info,
       if (LocaleCompare(value,"0") == 0)
         mng_info->write_png_compression_filter = 1;
 
-      if (LocaleCompare(value,"1") == 0)
+      else if (LocaleCompare(value,"1") == 0)
         mng_info->write_png_compression_filter = 2;
 
       else if (LocaleCompare(value,"2") == 0)
@@ -12526,7 +12746,7 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
         (void) LogMagickEvent(CoderEvent,GetMagickModule(),
           "      Image depth: %.20g",(double) p->depth);
 
-        if (p->alpha_trait)
+        if (p->alpha_trait == BlendPixelTrait)
           (void) LogMagickEvent(CoderEvent,GetMagickModule(),
             "      Matte: True");
 
@@ -12620,11 +12840,12 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
         if (next_image->page.x || next_image->page.y)
           need_defi=MagickTrue;
 
-        if (next_image->alpha_trait)
+        if (next_image->alpha_trait == BlendPixelTrait)
           need_matte=MagickTrue;
 
         if ((int) next_image->dispose >= BackgroundDispose)
-          if (next_image->alpha_trait || next_image->page.x || next_image->page.y ||
+          if ((next_image->alpha_trait == BlendPixelTrait) ||
+               next_image->page.x || next_image->page.y ||
               ((next_image->columns < mng_info->page.width) &&
                (next_image->rows < mng_info->page.height)))
             mng_info->need_fram=MagickTrue;
@@ -12996,8 +13217,8 @@ static MagickBooleanType WriteMNGImage(const ImageInfo *image_info,Image *image,
        Write MNG BACK chunk and global bKGD chunk, if the image is transparent
        or does not cover the entire frame.
      */
-     if (write_mng && (image->alpha_trait || image->page.x > 0 ||
-         image->page.y > 0 || (image->page.width &&
+     if (write_mng && ((image->alpha_trait == BlendPixelTrait) ||
+         image->page.x > 0 || image->page.y > 0 || (image->page.width &&
          (image->page.width+image->page.x < mng_info->page.width))
          || (image->page.height && (image->page.height+image->page.y
          < mng_info->page.height))))