PNG re-work: read 16-bit PNG as 16-bit
authorTony Cook <tony@develop-help.com>
Fri, 6 Apr 2012 13:18:45 +0000 (23:18 +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 2503b35..142baae 100644 (file)
@@ -11,6 +11,9 @@ static int CC2C[PNG_COLOR_MASK_PALETTE|PNG_COLOR_MASK_COLOR|PNG_COLOR_MASK_ALPHA
 static i_img *
 read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
 
+static i_img *
+read_direct16(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
+
 static i_img *
 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
 
@@ -295,6 +298,9 @@ i_readpng_wiol(io_glue *ig) {
           && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
     im = read_bilevel(png_ptr, info_ptr, width, height);
   }
+  else if (bit_depth == 16) {
+    im = read_direct16(png_ptr, info_ptr, channels, width, height);
+  }
   else {
     im = read_direct8(png_ptr, info_ptr, channels, width, height);
   }
@@ -375,6 +381,74 @@ read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
   return im;
 }
 
+static i_img *
+read_direct16(png_structp png_ptr, png_infop info_ptr, int channels,
+            i_img_dim width, i_img_dim height) {
+  i_img * volatile vim = NULL;
+  int color_type = png_get_color_type(png_ptr, info_ptr);
+  i_img_dim x, y;
+  int number_passes, pass;
+  i_img *im;
+  unsigned char *line;
+  unsigned char * volatile vline = NULL;
+  unsigned *bits_line;
+  unsigned * volatile vbits_line = NULL;
+  size_t row_bytes;
+
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    if (vim) i_img_destroy(vim);
+    if (vline) myfree(vline);
+    if (vbits_line) myfree(vbits_line);
+
+    return NULL;
+  }
+
+  number_passes = png_set_interlace_handling(png_ptr);
+  mm_log((1,"number of passes=%d\n",number_passes));
+
+  if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+    channels++;
+    mm_log((1, "image has transparency, adding alpha: channels = %d\n", channels));
+    png_set_expand(png_ptr);
+  }
+  
+  png_read_update_info(png_ptr, info_ptr);
+  
+  im = vim = i_img_16_new(width,height,channels);
+  if (!im) {
+    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+    return NULL;
+  }
+  
+  row_bytes = png_get_rowbytes(png_ptr, info_ptr);
+  line = vline = mymalloc(row_bytes);
+  memset(line, 0, row_bytes);
+  bits_line = vbits_line = mymalloc(sizeof(unsigned) * width * channels);
+  for (pass = 0; pass < number_passes; pass++) {
+    for (y = 0; y < height; y++) {
+      if (pass > 0) {
+       i_gsamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
+       for (x = 0; x < width * channels; ++x) {
+         line[x*2] = bits_line[x] >> 8;
+         line[x*2+1] = bits_line[x] & 0xff;
+       }
+      }
+      png_read_row(png_ptr,(png_bytep)line, NULL);
+      for (x = 0; x < width * channels; ++x)
+       bits_line[x] = (line[x*2] << 8) + line[x*2+1];
+      i_psamp_bits(im, 0, width, y, bits_line, NULL, channels, 16);
+    }
+  }
+  myfree(line);
+  myfree(bits_line);
+  vline = NULL;
+  vbits_line = NULL;
+  
+  png_read_end(png_ptr, info_ptr); 
+
+  return im;
+}
+
 static i_img *
 read_bilevel(png_structp png_ptr, png_infop info_ptr,
             i_img_dim width, i_img_dim height) {
index badc09a..a23cde4 100644 (file)
@@ -273,9 +273,7 @@ SKIP:
   is($im->getchannels, 3, "check channel count");
   is($im->type, "direct", "check type");
   is($im->tags(name => "png_interlace"), 0, "check png_interlace tag");
-  local $TODO = "Not yet implemented";
   is($im->bits, 16, "check bits");
-  undef $TODO;
   is($im->tags(name => "png_bits"), 16, "check png_bits tag");
 }