]> git.imager.perl.org - imager.git/blobdiff - TIFF/imtiff.c
changes note for sub-module --verbose handling
[imager.git] / TIFF / imtiff.c
index 75d824408bcc808d7d02e3fe63f51598e99f3db6..1f5aa06c071b084be786989eefaab0bfb46dda51 100644 (file)
@@ -50,8 +50,8 @@ struct tag_name {
   uint32 tag;
 };
 
-static i_img *read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete);
-static i_img *read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete);
+static i_img *read_one_rgb_tiled(TIFF *tif, i_img_dim width, i_img_dim height, int allow_incomplete);
+static i_img *read_one_rgb_lines(TIFF *tif, i_img_dim width, i_img_dim height, int allow_incomplete);
 
 static struct tag_name text_tag_names[] =
 {
@@ -100,8 +100,8 @@ typedef int (*read_setup_t)(read_state_t *state);
    the raster buffer, (for tiles against the right side of the
    image) */
 
-typedef int (*read_putter_t)(read_state_t *state, int x, int y, int width, 
-                            int height, int extras);
+typedef int (*read_putter_t)(read_state_t *state, i_img_dim x, i_img_dim y,
+                            i_img_dim width, i_img_dim height, int extras);
 
 /* reads from a tiled or strip image and calls the putter.
    This may need a second type for handling non-contiguous images
@@ -112,7 +112,7 @@ struct read_state_tag {
   TIFF *tif;
   i_img *img;
   void *raster;
-  unsigned long pixels_read;
+  i_img_dim pixels_read;
   int allow_incomplete;
   void *line_buf;
   uint32 width, height;
@@ -136,39 +136,104 @@ static int tile_contig_getter(read_state_t *state, read_putter_t putter);
 static int strip_contig_getter(read_state_t *state, read_putter_t putter);
 
 static int setup_paletted(read_state_t *state);
-static int paletted_putter8(read_state_t *, int, int, int, int, int);
-static int paletted_putter4(read_state_t *, int, int, int, int, int);
+static int paletted_putter8(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
+static int paletted_putter4(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_16_rgb(read_state_t *state);
 static int setup_16_grey(read_state_t *state);
-static int putter_16(read_state_t *, int, int, int, int, int);
+static int putter_16(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_8_rgb(read_state_t *state);
 static int setup_8_grey(read_state_t *state);
-static int putter_8(read_state_t *, int, int, int, int, int);
+static int putter_8(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_32_rgb(read_state_t *state);
 static int setup_32_grey(read_state_t *state);
-static int putter_32(read_state_t *, int, int, int, int, int);
+static int putter_32(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_bilevel(read_state_t *state);
-static int putter_bilevel(read_state_t *, int, int, int, int, int);
+static int putter_bilevel(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_cmyk8(read_state_t *state);
-static int putter_cmyk8(read_state_t *, int, int, int, int, int);
+static int putter_cmyk8(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
 
 static int setup_cmyk16(read_state_t *state);
-static int putter_cmyk16(read_state_t *, int, int, int, int, int);
+static int putter_cmyk16(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_dim, int);
+static void
+rgb_channels(read_state_t *state, int *out_channels);
+static void
+grey_channels(read_state_t *state, int *out_channels);
+static void
+cmyk_channels(read_state_t *state, int *out_channels);
+static void
+fallback_rgb_channels(TIFF *tif, i_img_dim width, i_img_dim height, int *channels, int *alpha_chan);
 
 static const int text_tag_count = 
   sizeof(text_tag_names) / sizeof(*text_tag_names);
 
+#if TIFFLIB_VERSION >= 20051230
+#define USE_EXT_WARN_HANDLER
+#endif
+
+#define TIFFIO_MAGIC 0xC6A340CC
+
 static void error_handler(char const *module, char const *fmt, va_list ap) {
   mm_log((1, "tiff error fmt %s\n", fmt));
   i_push_errorvf(0, fmt, ap);
 }
 
+typedef struct {
+  unsigned magic;
+  io_glue *ig;
+#ifdef USE_EXT_WARN_HANDLER
+  char *warn_buffer;
+  size_t warn_size;
+#endif
+} tiffio_context_t;
+
+static void
+tiffio_context_init(tiffio_context_t *c, io_glue *ig);
+static void
+tiffio_context_final(tiffio_context_t *c);
+
 #define WARN_BUFFER_LIMIT 10000
+
+#ifdef USE_EXT_WARN_HANDLER
+
+static void
+warn_handler_ex(thandle_t h, const char *module, const char *fmt, va_list ap) {
+  tiffio_context_t *c = (tiffio_context_t *)h;
+  char buf[200];
+
+  if (c->magic != TIFFIO_MAGIC)
+    return;
+
+  buf[0] = '\0';
+#ifdef IMAGER_VSNPRINTF
+  vsnprintf(buf, sizeof(buf), fmt, ap);
+#else
+  vsprintf(buf, fmt, ap);
+#endif
+  mm_log((1, "tiff warning %s\n", buf));
+
+  if (!c->warn_buffer || strlen(c->warn_buffer)+strlen(buf)+2 > c->warn_size) {
+    size_t new_size = c->warn_size + strlen(buf) + 2;
+    char *old_buffer = c->warn_buffer;
+    if (new_size > WARN_BUFFER_LIMIT) {
+      new_size = WARN_BUFFER_LIMIT;
+    }
+    c->warn_buffer = myrealloc(c->warn_buffer, new_size);
+    if (!old_buffer) c->warn_buffer[0] = '\0';
+    c->warn_size = new_size;
+  }
+  if (strlen(c->warn_buffer)+strlen(buf)+2 <= c->warn_size) {
+    strcat(c->warn_buffer, buf);
+    strcat(c->warn_buffer, "\n");
+  }
+}
+
+#else
+
 static char *warn_buffer = NULL;
 static int warn_buffer_size = 0;
 
@@ -176,7 +241,7 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) {
   char buf[1000];
 
   buf[0] = '\0';
-#ifdef HAVE_SNPRINTF
+#ifdef IMAGER_VSNPRINTF
   vsnprintf(buf, sizeof(buf), fmt, ap);
 #else
   vsprintf(buf, fmt, ap);
@@ -199,10 +264,12 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) {
   }
 }
 
+#endif
+
 static int save_tiff_tags(TIFF *tif, i_img *im);
 
 static void 
-pack_4bit_to(unsigned char *dest, const unsigned char *src, int count);
+pack_4bit_to(unsigned char *dest, const unsigned char *src, i_img_dim count);
 
 
 static toff_t sizeproc(thandle_t x) {
@@ -225,8 +292,8 @@ Compatability for 64 bit systems like latest freebsd (internal)
 static
 toff_t
 comp_seek(thandle_t h, toff_t o, int w) {
-  io_glue *ig = (io_glue*)h;
-  return (toff_t) ig->seekcb(ig, o, w);
+  io_glue *ig = ((tiffio_context_t *)h)->ig;
+  return (toff_t) i_io_seek(ig, o, w);
 }
 
 /*
@@ -260,6 +327,21 @@ comp_munmap(thandle_t h, tdata_t p, toff_t off) {
   /* do nothing */
 }
 
+static tsize_t
+comp_read(thandle_t h, tdata_t p, tsize_t size) {
+  return i_io_read(((tiffio_context_t *)h)->ig, p, size);
+}
+
+static tsize_t
+comp_write(thandle_t h, tdata_t p, tsize_t size) {
+  return i_io_write(((tiffio_context_t *)h)->ig, p, size);
+}
+
+static int
+comp_close(thandle_t h) {
+  return i_io_close(((tiffio_context_t *)h)->ig);
+}
+
 static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   i_img *im;
   uint32 width, height;
@@ -278,6 +360,9 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   read_setup_t setupf = NULL;
   read_getter_t getterf = NULL;
   read_putter_t putterf = NULL;
+  int channels = MAXCHANNELS;
+  size_t sample_size = ~0; /* force failure if some code doesn't set it */
+  i_img_dim total_pixels;
 
   error = 0;
 
@@ -294,6 +379,16 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
   mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
 
+  total_pixels = width * height;
+  memset(&state, 0, sizeof(state));
+  state.tif = tif;
+  state.allow_incomplete = allow_incomplete;
+  state.width = width;
+  state.height = height;
+  state.bits_per_sample = bits_per_sample;
+  state.samples_per_pixel = samples_per_pixel;
+  state.photometric = photometric;
+
   /* yes, this if() is horrible */
   if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
     setupf = setup_paletted;
@@ -303,38 +398,53 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
       putterf = paletted_putter4;
     else
       mm_log((1, "unsupported paletted bits_per_sample %d\n", bits_per_sample));
+
+    sample_size = sizeof(i_sample_t);
+    channels = 1;
   }
   else if (bits_per_sample == 16 
           && photometric == PHOTOMETRIC_RGB
           && samples_per_pixel >= 3) {
     setupf = setup_16_rgb;
     putterf = putter_16;
+    sample_size = 2;
+    rgb_channels(&state, &channels);
   }
   else if (bits_per_sample == 16
           && photometric == PHOTOMETRIC_MINISBLACK) {
     setupf = setup_16_grey;
     putterf = putter_16;
+    sample_size = 2;
+    grey_channels(&state, &channels);
   }
   else if (bits_per_sample == 8
           && photometric == PHOTOMETRIC_MINISBLACK) {
     setupf = setup_8_grey;
     putterf = putter_8;
+    sample_size = 1;
+    grey_channels(&state, &channels);
   }
   else if (bits_per_sample == 8
           && photometric == PHOTOMETRIC_RGB) {
     setupf = setup_8_rgb;
     putterf = putter_8;
+    sample_size = 1;
+    rgb_channels(&state, &channels);
   }
   else if (bits_per_sample == 32 
           && photometric == PHOTOMETRIC_RGB
           && samples_per_pixel >= 3) {
     setupf = setup_32_rgb;
     putterf = putter_32;
+    sample_size = sizeof(i_fsample_t);
+    rgb_channels(&state, &channels);
   }
   else if (bits_per_sample == 32
           && photometric == PHOTOMETRIC_MINISBLACK) {
     setupf = setup_32_grey;
     putterf = putter_32;
+    sample_size = sizeof(i_fsample_t);
+    grey_channels(&state, &channels);
   }
   else if (bits_per_sample == 1
           && (photometric == PHOTOMETRIC_MINISBLACK
@@ -342,6 +452,8 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
           && samples_per_pixel == 1) {
     setupf = setup_bilevel;
     putterf = putter_bilevel;
+    sample_size = sizeof(i_palidx);
+    channels = 1;
   }
   else if (bits_per_sample == 8
           && photometric == PHOTOMETRIC_SEPARATED
@@ -349,6 +461,8 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
           && samples_per_pixel >= 4) {
     setupf = setup_cmyk8;
     putterf = putter_cmyk8;
+    sample_size = 1;
+    cmyk_channels(&state, &channels);
   }
   else if (bits_per_sample == 16
           && photometric == PHOTOMETRIC_SEPARATED
@@ -356,7 +470,19 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
           && samples_per_pixel >= 4) {
     setupf = setup_cmyk16;
     putterf = putter_cmyk16;
+    sample_size = 2;
+    cmyk_channels(&state, &channels);
+  }
+  else {
+    int alpha;
+    fallback_rgb_channels(tif, width, height, &channels, &alpha);
+    sample_size = 1;
   }
+
+  if (!i_int_check_image_file_limits(width, height, channels, sample_size)) {
+    return NULL;
+  }
+
   if (tiled) {
     if (planar_config == PLANARCONFIG_CONTIG)
       getterf = tile_contig_getter;
@@ -366,15 +492,6 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
       getterf = strip_contig_getter;
   }
   if (setupf && getterf && putterf) {
-    unsigned long total_pixels = (unsigned long)width * height;
-    memset(&state, 0, sizeof(state));
-    state.tif = tif;
-    state.allow_incomplete = allow_incomplete;
-    state.width = width;
-    state.height = height;
-    state.bits_per_sample = bits_per_sample;
-    state.samples_per_pixel = samples_per_pixel;
-    state.photometric = photometric;
 
     if (!setupf(&state))
       return NULL;
@@ -462,10 +579,20 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   }
 
   i_tags_set(&im->tags, "i_format", "tiff", 4);
+#ifdef USE_EXT_WARN_HANDLER
+  {
+    tiffio_context_t *ctx = TIFFClientdata(tif);
+    if (ctx->warn_buffer && ctx->warn_buffer[0]) {
+      i_tags_set(&im->tags, "i_warning", ctx->warn_buffer, -1);
+      ctx->warn_buffer[0] = '\0';
+    }
+  }
+#else
   if (warn_buffer && *warn_buffer) {
     i_tags_set(&im->tags, "i_warning", warn_buffer, -1);
     *warn_buffer = '\0';
   }
+#endif
 
   for (i = 0; i < compress_value_count; ++i) {
     if (compress_values[i].tag == compress) {
@@ -487,45 +614,64 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
   TIFFErrorHandler old_warn_handler;
+#ifdef USE_EXT_WARN_HANDLER
+  TIFFErrorHandlerExt old_ext_warn_handler;
+#endif
   i_img *im;
+  int current_page;
+  tiffio_context_t ctx;
 
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
+#ifdef USE_EXT_WARN_HANDLER
+  old_warn_handler = TIFFSetWarningHandler(NULL);
+  old_ext_warn_handler = TIFFSetWarningHandlerExt(warn_handler_ex);
+#else
   old_warn_handler = TIFFSetWarningHandler(warn_handler);
   if (warn_buffer)
     *warn_buffer = '\0';
+#endif
 
   /* Add code to get the filename info from the iolayer */
   /* Also add code to check for mmapped code */
 
   mm_log((1, "i_readtiff_wiol(ig %p, allow_incomplete %d, page %d)\n", ig, allow_incomplete, page));
   
+  tiffio_context_init(&ctx, ig);
   tif = TIFFClientOpen("(Iolayer)", 
                       "rm", 
-                      (thandle_t) ig,
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc) comp_seek,
-                      (TIFFCloseProc) ig->closecb,
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc) comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx,
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close,
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
   if (!tif) {
     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
     i_push_error(0, "Error opening file");
     TIFFSetErrorHandler(old_handler);
     TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+    TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
+    tiffio_context_final(&ctx);
     return NULL;
   }
 
-  if (page != 0) {
-    if (!TIFFSetDirectory(tif, page)) {
+  for (current_page = 0; current_page < page; ++current_page) {
+    if (!TIFFReadDirectory(tif)) {
       mm_log((1, "i_readtiff_wiol: Unable to switch to directory %d\n", page));
       i_push_errorf(0, "could not switch to page %d", page);
       TIFFSetErrorHandler(old_handler);
       TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+    TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
       TIFFClose(tif);
+      tiffio_context_final(&ctx);
       return NULL;
     }
   }
@@ -535,7 +681,12 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
   if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
   TIFFSetErrorHandler(old_handler);
   TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+    TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
   TIFFClose(tif);
+  tiffio_context_final(&ctx);
+
   return im;
 }
 
@@ -551,15 +702,25 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
   TIFFErrorHandler old_warn_handler;
+#ifdef USE_EXT_WARN_HANDLER
+  TIFFErrorHandlerExt old_ext_warn_handler;
+#endif
   i_img **results = NULL;
   int result_alloc = 0;
-  int dirnum = 0;
+  tiffio_context_t ctx;
 
   i_clear_error();
   old_handler = TIFFSetErrorHandler(error_handler);
+#ifdef USE_EXT_WARN_HANDLER
+  old_warn_handler = TIFFSetWarningHandler(NULL);
+  old_ext_warn_handler = TIFFSetWarningHandlerExt(warn_handler_ex);
+#else
   old_warn_handler = TIFFSetWarningHandler(warn_handler);
   if (warn_buffer)
     *warn_buffer = '\0';
+#endif
+
+  tiffio_context_init(&ctx, ig);
 
   /* Add code to get the filename info from the iolayer */
   /* Also add code to check for mmapped code */
@@ -568,20 +729,24 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
   
   tif = TIFFClientOpen("(Iolayer)", 
                       "rm", 
-                      (thandle_t) ig,
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc) comp_seek,
-                      (TIFFCloseProc) ig->closecb,
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc) comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx,
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close,
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
   if (!tif) {
     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
     i_push_error(0, "Error opening file");
     TIFFSetErrorHandler(old_handler);
     TIFFSetWarningHandler(old_warn_handler);
+#ifdef USE_EXT_WARN_HANDLER
+    TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
+    tiffio_context_final(&ctx);
     return NULL;
   }
 
@@ -607,11 +772,16 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
       }
     }
     results[*count-1] = im;
-  } while (TIFFSetDirectory(tif, ++dirnum));
+  } while (TIFFReadDirectory(tif));
 
   TIFFSetWarningHandler(old_warn_handler);
   TIFFSetErrorHandler(old_handler);
+#ifdef USE_EXT_WARN_HANDLER
+    TIFFSetWarningHandlerExt(old_ext_warn_handler);
+#endif
   TIFFClose(tif);
+  tiffio_context_final(&ctx);
+
   return results;
 }
 
@@ -629,6 +799,11 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
   width    = im->xsize;
   height   = im->ysize;
 
+  if (width != im->xsize || height != im->ysize) {
+    i_push_error(0, "image too large for TIFF");
+    return 0;
+  }
+
   switch (im->channels) {
   case 1:
   case 2:
@@ -851,7 +1026,7 @@ write_one_bilevel(TIFF *tif, i_img *im, int zero_is_white) {
   unsigned char *in_row;
   unsigned char *out_row;
   unsigned out_size;
-  int x, y;
+  i_img_dim x, y;
   int invert;
 
   mm_log((1, "tiff - write_one_bilevel(tif %p, im %p, zero_is_white %d)\n", 
@@ -956,7 +1131,7 @@ write_one_paletted8(TIFF *tif, i_img *im) {
   uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
   unsigned char *out_row;
   unsigned out_size;
-  int y;
+  i_img_dim y;
 
   mm_log((1, "tiff - write_one_paletted8(tif %p, im %p)\n", tif, im));
 
@@ -1000,8 +1175,8 @@ write_one_paletted4(TIFF *tif, i_img *im) {
   uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
   unsigned char *in_row;
   unsigned char *out_row;
-  unsigned out_size;
-  int y;
+  size_t out_size;
+  i_img_dim y;
 
   mm_log((1, "tiff - write_one_paletted4(tif %p, im %p)\n", tif, im));
 
@@ -1084,7 +1259,7 @@ write_one_32(TIFF *tif, i_img *im) {
   unsigned *in_row;
   size_t out_size;
   uint32 *out_row;
-  int y;
+  i_img_dim y;
   size_t sample_count = im->xsize * im->channels;
   size_t sample_index;
     
@@ -1128,7 +1303,7 @@ write_one_16(TIFF *tif, i_img *im) {
   unsigned *in_row;
   size_t out_size;
   uint16 *out_row;
-  int y;
+  i_img_dim y;
   size_t sample_count = im->xsize * im->channels;
   size_t sample_index;
     
@@ -1171,7 +1346,7 @@ write_one_8(TIFF *tif, i_img *im) {
   uint16 compress = get_compression(im, COMPRESSION_PACKBITS);
   size_t out_size;
   unsigned char *out_row;
-  int y;
+  i_img_dim y;
   size_t sample_count = im->xsize * im->channels;
     
   mm_log((1, "tiff - write_one_8(tif %p, im %p)\n", tif, im));
@@ -1210,6 +1385,11 @@ i_writetiff_low(TIFF *tif, i_img *im) {
   height   = im->ysize;
   channels = im->channels;
 
+  if (width != im->xsize || height != im->ysize) {
+    i_push_error(0, "image too large for TIFF");
+    return 0;
+  }
+
   mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d, bits=%d\n", width, height, channels, im->bits));
   if (im->type == i_palette_type) {
     mm_log((1, "i_writetiff_low: paletted, colors=%d\n", i_colorcount(im)));
@@ -1264,25 +1444,26 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
   int i;
+  tiffio_context_t ctx;
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
-  mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n", 
+  mm_log((1, "i_writetiff_multi_wiol(ig %p, imgs %p, count %d)\n", 
           ig, imgs, count));
 
-  /* FIXME: Enable the mmap interface */
+  tiffio_context_init(&ctx, ig);
   
   tif = TIFFClientOpen("No name", 
                       "wm",
-                      (thandle_t) ig
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc)      comp_seek,
-                      (TIFFCloseProc)     ig->closecb
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc)   comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
 
 
@@ -1290,6 +1471,7 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
     mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n"));
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
     return 0;
   }
 
@@ -1297,6 +1479,7 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
     if (!i_writetiff_low(tif, imgs[i])) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
+      tiffio_context_final(&ctx);
       return 0;
     }
 
@@ -1304,12 +1487,17 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
       i_push_error(0, "Cannot write TIFF directory");
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
+      tiffio_context_final(&ctx);
       return 0;
     }
   }
 
   TIFFSetErrorHandler(old_handler);
   (void) TIFFClose(tif);
+  tiffio_context_final(&ctx);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1332,25 +1520,26 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
   TIFF* tif;
   int i;
   TIFFErrorHandler old_handler;
+  tiffio_context_t ctx;
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
-  mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n", 
+  mm_log((1, "i_writetiff_multi_wiol(ig %p, imgs %p, count %d)\n", 
           ig, imgs, count));
 
-  /* FIXME: Enable the mmap interface */
+  tiffio_context_init(&ctx, ig);
   
   tif = TIFFClientOpen("No name", 
                       "wm",
-                      (thandle_t) ig
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc)      comp_seek,
-                      (TIFFCloseProc)     ig->closecb
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc)   comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
 
 
@@ -1358,6 +1547,7 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
     mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
     return 0;
   }
 
@@ -1365,6 +1555,7 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
     if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
+      tiffio_context_final(&ctx);
       return 0;
     }
 
@@ -1372,12 +1563,17 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
       i_push_error(0, "Cannot write TIFF directory");
       TIFFClose(tif);
       TIFFSetErrorHandler(old_handler);
+      tiffio_context_final(&ctx);
       return 0;
     }
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+  tiffio_context_final(&ctx);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1396,42 +1592,49 @@ undef_int
 i_writetiff_wiol(i_img *img, io_glue *ig) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
+  tiffio_context_t ctx;
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
-  mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig));
+  mm_log((1, "i_writetiff_wiol(img %p, ig %p)\n", img, ig));
 
-  /* FIXME: Enable the mmap interface */
+  tiffio_context_init(&ctx, ig);
 
   tif = TIFFClientOpen("No name", 
                       "wm",
-                      (thandle_t) ig
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc)      comp_seek,
-                      (TIFFCloseProc)     ig->closecb
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc)   comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
 
 
   if (!tif) {
     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
     i_push_error(0, "Could not create TIFF object");
+    tiffio_context_final(&ctx);
     TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   if (!i_writetiff_low(tif, img)) {
     TIFFClose(tif);
+    tiffio_context_final(&ctx);
     TIFFSetErrorHandler(old_handler);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1457,24 +1660,25 @@ undef_int
 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
+  tiffio_context_t ctx;
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
-  mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", im, ig));
+  mm_log((1, "i_writetiff_wiol(img %p, ig %p)\n", im, ig));
 
-  /* FIXME: Enable the mmap interface */
+  tiffio_context_init(&ctx, ig);
   
   tif = TIFFClientOpen("No name", 
                       "wm",
-                      (thandle_t) ig
-                      (TIFFReadWriteProc) ig->readcb,
-                      (TIFFReadWriteProc) ig->writecb,
-                      (TIFFSeekProc)      comp_seek,
-                      (TIFFCloseProc)     ig->closecb
-                      ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
-                      (TIFFMapFileProc)   comp_mmap,
-                      (TIFFUnmapFileProc) comp_munmap);
+                      (thandle_t) &ctx
+                      comp_read,
+                      comp_write,
+                      comp_seek,
+                      comp_close
+                      sizeproc,
+                      comp_mmap,
+                      comp_munmap);
   
 
 
@@ -1482,17 +1686,23 @@ i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
     i_push_error(0, "Could not create TIFF object");
     TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
     return 0;
   }
 
   if (!i_writetiff_low_faxable(tif, im, fine)) {
     TIFFClose(tif);
     TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+  tiffio_context_final(&ctx);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1510,14 +1720,14 @@ static int save_tiff_tags(TIFF *tif, i_img *im) {
       }
     }
   }
+
   return 1;
 }
 
 
 static void
 unpack_4bit_to(unsigned char *dest, const unsigned char *src, 
-              int src_byte_count) {
+              size_t src_byte_count) {
   while (src_byte_count > 0) {
     *dest++ = *src >> 4;
     *dest++ = *src++ & 0xf;
@@ -1526,7 +1736,7 @@ unpack_4bit_to(unsigned char *dest, const unsigned char *src,
 }
 
 static void pack_4bit_to(unsigned char *dest, const unsigned char *src, 
-                        int pixel_count) {
+                        i_img_dim pixel_count) {
   int i = 0;
   while (i < pixel_count) {
     if ((i & 1) == 0) {
@@ -1539,10 +1749,19 @@ static void pack_4bit_to(unsigned char *dest, const unsigned char *src,
   }
 }
 
-static i_img *
-make_rgb(TIFF *tif, int width, int height, int *alpha_chan) {
+/*
+=item fallback_rgb_channels
+
+Calculate the number of output channels when we fallback to the RGBA
+family of functions.
+
+=cut
+*/
+
+static void
+fallback_rgb_channels(TIFF *tif, i_img_dim width, i_img_dim height, int *channels, int *alpha_chan) {
   uint16 photometric;
-  uint16 channels, in_channels;
+  uint16 in_channels;
   uint16 extra_count;
   uint16 *extras;
 
@@ -1551,7 +1770,7 @@ make_rgb(TIFF *tif, int width, int height, int *alpha_chan) {
 
   switch (photometric) {
   case PHOTOMETRIC_SEPARATED:
-    channels = 3;
+    *channels = 3;
     break;
   
   case PHOTOMETRIC_MINISWHITE:
@@ -1559,11 +1778,11 @@ make_rgb(TIFF *tif, int width, int height, int *alpha_chan) {
     /* the TIFF RGBA functions expand single channel grey into RGB,
        so reduce it, we move the alpha channel into the right place 
        if needed */
-    channels = 1;
+    *channels = 1;
     break;
 
   default:
-    channels = 3;
+    *channels = 3;
     break;
   }
   /* TIFF images can have more than one alpha channel, but Imager can't
@@ -1572,14 +1791,21 @@ make_rgb(TIFF *tif, int width, int height, int *alpha_chan) {
   *alpha_chan = 0;
   if (TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)
       && extra_count) {
-    *alpha_chan = channels++;
+    *alpha_chan = (*channels)++;
   }
+}
+
+static i_img *
+make_rgb(TIFF *tif, i_img_dim width, i_img_dim height, int *alpha_chan) {
+  int channels = 0;
+
+  fallback_rgb_channels(tif, width, height, &channels, alpha_chan);
 
   return i_img_8_new(width, height, channels);
 }
 
 static i_img *
-read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete) {
+read_one_rgb_lines(TIFF *tif, i_img_dim width, i_img_dim height, int allow_incomplete) {
   i_img *im;
   uint32* raster = NULL;
   uint32 rowsperstrip, row;
@@ -1750,7 +1976,7 @@ myTIFFReadRGBATile(TIFFRGBAImage *img, uint32 col, uint32 row, uint32 * raster)
 }
 
 static i_img *
-read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete) {
+read_one_rgb_tiled(TIFF *tif, i_img_dim width, i_img_dim height, int allow_incomplete) {
   i_img *im;
   uint32* raster = NULL;
   int ok = 1;
@@ -1968,10 +2194,10 @@ strip_contig_getter(read_state_t *state, read_putter_t putter) {
 }
 
 static int 
-paletted_putter8(read_state_t *state, int x, int y, int width, int height, int extras) {
+paletted_putter8(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, int extras) {
   unsigned char *p = state->raster;
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
     i_ppal(state->img, x, x + width, y, p);
     p += width + extras;
@@ -1983,7 +2209,7 @@ paletted_putter8(read_state_t *state, int x, int y, int width, int height, int e
 }
 
 static int 
-paletted_putter4(read_state_t *state, int x, int y, int width, int height, int extras) {
+paletted_putter4(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, int extras) {
   uint32 img_line_size = (width + 1) / 2;
   uint32 skip_line_size = (width + extras + 1) / 2;
   unsigned char *p = state->raster;
@@ -1991,7 +2217,7 @@ paletted_putter4(read_state_t *state, int x, int y, int width, int height, int e
   if (!state->line_buf)
     state->line_buf = mymalloc(state->width);
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
     unpack_4bit_to(state->line_buf, p, img_line_size);
     i_ppal(state->img, x, x + width, y, state->line_buf);
@@ -2121,14 +2347,14 @@ setup_16_grey(read_state_t *state) {
 }
 
 static int 
-putter_16(read_state_t *state, int x, int y, int width, int height, 
+putter_16(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
          int row_extras) {
   uint16 *p = state->raster;
   int out_chan = state->img->channels;
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     int ch;
     unsigned *outp = state->line_buf;
 
@@ -2185,14 +2411,14 @@ setup_8_grey(read_state_t *state) {
 }
 
 static int 
-putter_8(read_state_t *state, int x, int y, int width, int height, 
+putter_8(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
          int row_extras) {
   unsigned char *p = state->raster;
   int out_chan = state->img->channels;
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     int ch;
     i_color *outp = state->line_buf;
 
@@ -2251,14 +2477,14 @@ setup_32_grey(read_state_t *state) {
 }
 
 static int 
-putter_32(read_state_t *state, int x, int y, int width, int height, 
+putter_32(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
          int row_extras) {
   uint32 *p = state->raster;
   int out_chan = state->img->channels;
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     int ch;
     i_fcolor *outp = state->line_buf;
 
@@ -2308,16 +2534,16 @@ setup_bilevel(read_state_t *state) {
 }
 
 static int 
-putter_bilevel(read_state_t *state, int x, int y, int width, int height, 
+putter_bilevel(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
               int row_extras) {
   unsigned char *line_in = state->raster;
   size_t line_size = (width + row_extras + 7) / 8;
   
   /* tifflib returns the bits in MSB2LSB order even when the file is
      in LSB2MSB, so we only need to handle MSB2LSB */
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     unsigned char *outp = state->line_buf;
     unsigned char *inp = line_in;
     unsigned mask = 0x80;
@@ -2398,13 +2624,13 @@ setup_cmyk8(read_state_t *state) {
 }
 
 static int 
-putter_cmyk8(read_state_t *state, int x, int y, int width, int height, 
+putter_cmyk8(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
               int row_extras) {
   unsigned char *p = state->raster;
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     int ch;
     i_color *outp = state->line_buf;
 
@@ -2454,16 +2680,16 @@ setup_cmyk16(read_state_t *state) {
 }
 
 static int 
-putter_cmyk16(read_state_t *state, int x, int y, int width, int height, 
+putter_cmyk16(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_dim height, 
               int row_extras) {
   uint16 *p = state->raster;
   int out_chan = state->img->channels;
 
   mm_log((4, "putter_cmyk16(%p, %d, %d, %d, %d, %d)\n", x, y, width, height, row_extras));
 
-  state->pixels_read += (unsigned long) width * height;
+  state->pixels_read += width * height;
   while (height > 0) {
-    int i;
+    i_img_dim i;
     int ch;
     unsigned *outp = state->line_buf;
 
@@ -2545,6 +2771,25 @@ myTIFFIsCODECConfigured(uint16 scheme) {
   return TIFFIsCODECConfigured(scheme);
 }
 
+static void
+tiffio_context_init(tiffio_context_t *c, io_glue *ig) {
+  c->magic = TIFFIO_MAGIC;
+  c->ig = ig;
+#ifdef USE_EXT_WARN_HANDLER
+  c->warn_buffer = NULL;
+  c->warn_size = 0;
+#endif
+}
+
+static void
+tiffio_context_final(tiffio_context_t *c) {
+  c->magic = TIFFIO_MAGIC;
+#ifdef USE_EXT_WARN_HANDLER
+  if (c->warn_buffer)
+    myfree(c->warn_buffer);
+#endif
+}
+
 /*
 =back