PNG re-work: support writing 16-bit/sample images
authorTony Cook <tony@develop-help.com>
Sat, 14 Apr 2012 02:52:22 +0000 (12:52 +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

index f3f2418..5f1d325 100644 (file)
@@ -24,6 +24,9 @@ read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim
 static int
 write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im);
 
+static int
+write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im);
+
 static int
 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits);
 
@@ -154,7 +157,7 @@ i_writepng_wiol(i_img *im, io_glue *ig) {
       fprintf(stderr, "Internal error, channels = %d\n", channels);
       abort();
     }
-    bits = 8;
+    bits = im->bits > 8 ? 16 : 8;
     mm_log((1, "i_writepng: direct output\n"));
   }
 
@@ -247,7 +250,13 @@ i_writepng_wiol(i_img *im, io_glue *ig) {
       return 0;
     }
   }
-  else  {
+  else if (bits == 16) {
+    if (!write_direct16(png_ptr, info_ptr, im)) {
+      png_destroy_write_struct(&png_ptr, &info_ptr);
+      return 0;
+    }
+  }
+  else {
     if (!write_direct8(png_ptr, info_ptr, im)) {
       png_destroy_write_struct(&png_ptr, &info_ptr);
       return 0;
@@ -800,6 +809,44 @@ write_direct8(png_structp png_ptr, png_infop info_ptr, i_img *im) {
   return 1;
 }
 
+static int
+write_direct16(png_structp png_ptr, png_infop info_ptr, i_img *im) {
+  unsigned *data, *volatile vdata = NULL;
+  unsigned char *tran_data, * volatile vtran_data = NULL;
+  i_img_dim samples_per_row = im->xsize * im->channels;
+  
+  i_img_dim y;
+
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    if (vdata)
+      myfree(vdata);
+    if (vtran_data)
+      myfree(vtran_data);
+
+    return 0;
+  }
+
+  png_write_info(png_ptr, info_ptr);
+
+  vdata = data = mymalloc(samples_per_row * sizeof(unsigned));
+  vtran_data = tran_data = mymalloc(samples_per_row * 2);
+  for (y = 0; y < im->ysize; y++) {
+    i_img_dim i;
+    unsigned char *p = tran_data;
+    i_gsamp_bits(im, 0, im->xsize, y, data, NULL, im->channels, 16);
+    for (i = 0; i < samples_per_row; ++i) {
+      p[0] = data[i] >> 8;
+      p[1] = data[i] & 0xff;
+      p += 2;
+    }
+    png_write_row(png_ptr, (png_bytep)tran_data);
+  }
+  myfree(tran_data);
+  myfree(data);
+
+  return 1;
+}
+
 static int
 write_paletted(png_structp png_ptr, png_infop info_ptr, i_img *im, int bits) {
   unsigned char *data, *volatile vdata = NULL;
index b390724..3638d5c 100644 (file)
@@ -2,7 +2,7 @@
 use strict;
 use Imager qw(:all);
 use Test::More;
-use Imager::Test qw(test_image_raw test_image is_image);
+use Imager::Test qw(test_image_raw test_image is_image is_imaged test_image_16 test_image_double);
 
 my $debug_writes = 1;
 
@@ -10,7 +10,7 @@ my $debug_writes = 1;
 
 init_log("testout/t102png.log",1);
 
-plan tests => 192;
+plan tests => 204;
 
 # this loads Imager::File::PNG too
 ok($Imager::formats{"png"}, "must have png format");
@@ -535,6 +535,39 @@ SKIP:
   is($in->tags(name => "png_bits"), 1, "1 bit representation");
 }
 
+SKIP:
+{
+  my $im = test_image_16();
+  ok($im->write(file => "testout/rgb16.png", type => "png"),
+     "write 16-bit/sample image")
+    or diag("Could not write rgb16.png: ".$im->errstr);
+  my $in = Imager->new(file => "testout/rgb16.png")
+    or diag("Could not read rgb16.png: ".Imager->errstr);
+  ok($in, "read rgb16.png back in")
+    or skip("Could not load image to check", 4);
+  is_imaged($in, $im, 0, "check image matches");
+  is($in->bits, 16, "check we got a 16-bit image");
+  is($in->type, "direct", "check it's direct");
+  is($in->tags(name => "png_bits"), 16, "check png_bits");
+}
+
+SKIP:
+{
+  my $im = test_image_double();
+  my $cmp = $im->to_rgb16;
+  ok($im->write(file => "testout/rgbdbl.png", type => "png"),
+     "write double/sample image - should write as 16-bit/sample")
+    or diag("Could not write rgbdbl.png: ".$im->errstr);
+  my $in = Imager->new(file => "testout/rgbdbl.png")
+    or diag("Could not read rgbdbl.png: ".Imager->errstr);
+  ok($in, "read pngdbl.png back in")
+    or skip("Could not load image to check", 4);
+  is_imaged($in, $cmp, 0, "check image matches");
+  is($in->bits, 16, "check we got a 16-bit image");
+  is($in->type, "direct", "check it's direct");
+  is($in->tags(name => "png_bits"), 16, "check png_bits");
+}
+
 sub limited_write {
   my ($limit) = @_;