PNG re-work: read 1 bit grey images as the common internal bilevel format
authorTony Cook <tony@develop-help.com>
Fri, 6 Apr 2012 07:30:39 +0000 (17:30 +1000)
committerTony Cook <tony@develop-help.com>
Sun, 29 Apr 2012 03:40:55 +0000 (13:40 +1000)
which is really paletted

Changes
PNG/impng.c
PNG/t/10png.t

diff --git a/Changes b/Changes
index 1017a99..b277b51 100644 (file)
--- a/Changes
+++ b/Changes
@@ -4,11 +4,15 @@ Imager release history.  Older releases can be found in Changes.old
    - improve error reporting
    - add png_interlace, png_bits tags
    - read paletted images as paletted images, including transparency
+   - read 1 bit greyscale images as a type suitable for other file
+     handlers to write as bilevel
 
   TODO:
     - flush output
     - documentation in C code
     - documentation of new tags, behaviour
+    - FIXME for read_paletted: do we need to scale the palette colours 
+      based on the alpha?
 
 Imager 0.90 - unreleased
 ===========
index 3927da9..2503b35 100644 (file)
@@ -14,6 +14,9 @@ read_direct8(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim wi
 static i_img *
 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels, i_img_dim width, i_img_dim height);
 
+static i_img *
+read_bilevel(png_structp png_ptr, png_infop info_ptr, i_img_dim width, i_img_dim height);
+
 unsigned
 i_png_lib_version(void) {
   return png_access_version_number();
@@ -203,7 +206,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);
+get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth);
 
 typedef struct {
   char *warnings;
@@ -287,12 +290,17 @@ i_readpng_wiol(io_glue *ig) {
   if (color_type == PNG_COLOR_TYPE_PALETTE) {
     im = read_paletted(png_ptr, info_ptr, channels, width, height);
   }
+  else if (color_type == PNG_COLOR_TYPE_GRAY
+          && bit_depth == 1
+          && !png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+    im = read_bilevel(png_ptr, info_ptr, width, height);
+  }
   else {
     im = read_direct8(png_ptr, info_ptr, channels, width, height);
   }
 
   if (im)
-    get_png_tags(im, png_ptr, info_ptr);
+    get_png_tags(im, png_ptr, info_ptr, bit_depth);
 
   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
 
@@ -367,6 +375,73 @@ read_direct8(png_structp png_ptr, png_infop info_ptr, int channels,
   return im;
 }
 
+static i_img *
+read_bilevel(png_structp png_ptr, png_infop info_ptr,
+            i_img_dim width, i_img_dim height) {
+  i_img * volatile vim = NULL;
+  i_img_dim x, y;
+  int number_passes, pass;
+  i_img *im;
+  unsigned char *line;
+  unsigned char * volatile vline = NULL;
+  i_color palette[2];
+
+  if (setjmp(png_jmpbuf(png_ptr))) {
+    if (vim) i_img_destroy(vim);
+    if (vline) myfree(vline);
+
+    return NULL;
+  }
+
+  number_passes = png_set_interlace_handling(png_ptr);
+  mm_log((1,"number of passes=%d\n",number_passes));
+
+  png_set_packing(png_ptr);
+
+  png_set_expand(png_ptr);  
+  
+  png_read_update_info(png_ptr, info_ptr);
+  
+  im = vim = i_img_pal_new(width, height, 1, 256);
+  if (!im) {
+    png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+    return NULL;
+  }
+
+  palette[0].channel[0] = palette[0].channel[1] = palette[0].channel[2] = 
+    palette[0].channel[3] = 0;
+  palette[1].channel[0] = palette[1].channel[1] = palette[1].channel[2] = 
+    palette[1].channel[3] = 255;
+  i_addcolors(im, palette, 2);
+  
+  line = vline = mymalloc(width);
+  memset(line, 0, width);
+  for (pass = 0; pass < number_passes; pass++) {
+    for (y = 0; y < height; y++) {
+      if (pass > 0) {
+       i_gpal(im, 0, width, y, line);
+       /* expand indexes back to 0/255 */
+       for (x = 0; x < width; ++x)
+         line[x] = line[x] ? 255 : 0;
+      }
+      png_read_row(png_ptr,(png_bytep)line, NULL);
+
+      /* back to palette indexes */
+      for (x = 0; x < width; ++x)
+       line[x] = line[x] ? 1 : 0;
+      i_ppal(im, 0, width, y, line);
+    }
+  }
+  myfree(line);
+  vline = NULL;
+  
+  png_read_end(png_ptr, info_ptr); 
+
+  return im;
+}
+
+/* FIXME: do we need to unscale palette color values from the 
+   supplied alphas? */
 static i_img *
 read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
              i_img_dim width, i_img_dim height) {
@@ -452,7 +527,7 @@ read_paletted(png_structp png_ptr, png_infop info_ptr, int channels,
 }
 
 static void
-get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
+get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr, int bit_depth) {
   png_uint_32 xres, yres;
   int unit_type;
 
@@ -482,7 +557,9 @@ get_png_tags(i_img *im, png_structp png_ptr, png_infop info_ptr) {
     break;
   }
 
-  i_tags_setn(&im->tags, "png_bits", png_get_bit_depth(png_ptr, info_ptr));
+  /* the various readers can call png_set_expand(), libpng will make
+     it's internal record of bit_depth at least 8 in that case */
+  i_tags_setn(&im->tags, "png_bits", bit_depth);
 }
 
 static void
index c0c39de..badc09a 100644 (file)
@@ -275,6 +275,7 @@ SKIP:
   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");
 }
 
@@ -284,7 +285,6 @@ SKIP:
      "read bilevel png");
   is($im->getchannels, 1, "check channel count");
   is($im->tags(name => "png_interlace"), 0, "check png_interlace tag");
-  local $TODO = "Not yet implemented";
   is($im->type, "paletted", "check type");
   ok($im->is_bilevel, "should be bilevel");
   is($im->tags(name => "png_bits"), 1, "check png_bits tag");