PNG re-work: process the bKGD chunk into an i_background tag
authorTony Cook <tony@develop-help.com>
Mon, 16 Apr 2012 09:05:53 +0000 (19:05 +1000)
committerTony Cook <tony@develop-help.com>
Sun, 29 Apr 2012 03:40:56 +0000 (13:40 +1000)
PNG/impng.c
PNG/t/10png.t
PNG/testimg/comment.png
PNG/testimg/cover.png
fileformatdocs/pngdump.pl

index 1d63ab3..6a3ba6b 100644 (file)
@@ -276,7 +276,7 @@ i_writepng_wiol(i_img *im, io_glue *ig) {
 }
 
 static void 
-get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth);
+get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth, int color_type);
 
 typedef struct {
   char *warnings;
@@ -370,7 +370,7 @@ i_readpng_wiol(io_glue *ig) {
   }
 
   if (im)
-    get_png_tags(im, png_ptr, info_ptr, bit_depth);
+    get_png_tags(im, png_ptr, info_ptr, bit_depth, color_type);
 
   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
 
@@ -685,7 +685,8 @@ text_tags[] = {
 static const int text_tags_count = sizeof(text_tags) / sizeof(*text_tags);
 
 static void
-get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
+get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr,
+            int bit_depth, int color_type) {
   png_uint_32 xres, yres;
   int unit_type;
 
@@ -806,6 +807,63 @@ get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth)
       i_tags_set(&im->tags, "png_time", time_formatted, -1);
     }
   }
+
+  {
+    png_color_16 *back;
+    i_color c;
+
+    if (png_get_bKGD(png_ptr, info_ptr, &back)) {
+      switch (color_type) {
+      case PNG_COLOR_TYPE_GRAY:
+      case PNG_COLOR_TYPE_GRAY_ALPHA:
+       {
+         /* lib png stores the raw gray value rather than scaling it
+            to 16-bit (or 8), we use 8-bit color for i_background */
+
+         int gray;
+         switch (bit_depth) {
+         case 16:
+           gray = back->gray >> 8;
+           break;
+         case 8:
+           gray = back->gray;
+           break;
+         case 4:
+           gray = 0x11 * back->gray;
+           break;
+         case 2:
+           gray = 0x55 * back->gray;
+           break;
+         case 1:
+           gray = back->gray ? 0xFF : 0;
+           break;
+         default:
+           gray = 0;
+         }
+         c.rgb.r = c.rgb.g = c.rgb.b = gray;
+         break;
+       }
+
+      case PNG_COLOR_TYPE_RGB:
+      case PNG_COLOR_TYPE_RGB_ALPHA:
+       {
+         c.rgb.r = bit_depth == 16 ? (back->red   >> 8) : back->red;
+         c.rgb.g = bit_depth == 16 ? (back->green >> 8) : back->green;
+         c.rgb.b = bit_depth == 16 ? (back->blue  >> 8) : back->blue;
+         break;
+       }
+
+      case PNG_COLOR_TYPE_PALETTE:
+       c.rgb.r = back->red;
+       c.rgb.g = back->green;
+       c.rgb.b = back->blue;
+       break;
+      }
+
+      c.rgba.a = 255;
+      i_tags_set_color(&im->tags, "i_background", 0, &c);
+    }
+  }
 }
 
 static int
index ff9fb57..e0a127e 100644 (file)
@@ -10,7 +10,7 @@ my $debug_writes = 1;
 
 init_log("testout/t102png.log",1);
 
-plan tests => 210;
+plan tests => 211;
 
 # this loads Imager::File::PNG too
 ok($Imager::formats{"png"}, "must have png format");
@@ -579,8 +579,10 @@ SKIP:
   is($im->tags(name => "png_interlace"), "0", "no interlace");
   is($im->tags(name => "png_interlace_name"), "none", "no interlace (text)");
   is($im->tags(name => "png_srgb_intent"), "0", "srgb perceptual");
-  is($im->tags(name => "png_time"), "2012-04-15T03:36:50",
+  is($im->tags(name => "png_time"), "2012-04-16T07:37:36",
      "modification time");
+  is($im->tags(name => "i_background"), "color(255,255,255,255)",
+     "background color");
 }
 
 sub limited_write {
index 8962fe1..0faf2c2 100644 (file)
Binary files a/PNG/testimg/comment.png and b/PNG/testimg/comment.png differ
index 2c54b4b..a13a2e0 100644 (file)
Binary files a/PNG/testimg/cover.png and b/PNG/testimg/cover.png differ
index 0a8905b..e79d964 100644 (file)
@@ -29,6 +29,7 @@ unless ($head eq "\x89PNG\x0d\x0A\cZ\x0A") {
 }
 
 my $colour_type;
+my $bits;
 my $sline_len;
 my $sline_left = 0;
 my $row = 0;
@@ -54,6 +55,7 @@ while (my ($dlen, $data, $len, $type, $payload, $crc) = read_chunk($fh)) {
   Interlace: $inter
 EOS
     $colour_type = $ct;
+    $bits = $d;
     my $channels = $ct == 2 ? 3 : $ct == 4 ? 2 : $ct == 6 ? 4 : 0;
     my $bitspp = $channels * $d;
     $sline_len = int((($w * $bitspp) + 7) / 8);
@@ -122,6 +124,20 @@ EOS
     my @when = unpack("nCCCCC", $payload);
     printf "  Date: %d-%02d-%02d %02d:%02d:%02d\n", @when;
   }
+  elsif ($type eq 'bKGD') {
+    if ($colour_type == 2 || $colour_type == 6) {
+      my @rgb = unpack("nnn", $payload);
+      printf "  Background: rgb$bits(%d,%d,%d)\n", @rgb;
+    }
+    elsif ($colour_type == 0 || $colour_type == 4) {
+      my $g = unpack("n", $payload);
+      printf "  Background: grey$bits(%d)\n", $g;
+    }
+    if ($colour_type == 3) {
+      my $index = unpack("C", $payload);
+      printf "  Background: index(%d)\n", $index;
+    }
+  }
   elsif ($type eq "IDAT" && $image) {
     $sline_len
       or die "IDAT before IHDR!?";