]> git.imager.perl.org - imager.git/blobdiff - TIFF/imtiff.c
mark mm_log() for the API with the right gcc -Wformat magic
[imager.git] / TIFF / imtiff.c
index 13f8650534affe865882c342974f1e8e1058f7df..c848fdaab3a8ca4a93f96f19e3715b5a71da4050 100644 (file)
@@ -159,16 +159,81 @@ static int putter_cmyk8(read_state_t *, i_img_dim, i_img_dim, i_img_dim, i_img_d
 
 static int setup_cmyk16(read_state_t *state);
 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,6 +264,15 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) {
   }
 }
 
+#endif
+
+static i_mutex_t mutex;
+
+void
+i_tiff_init(void) {
+  mutex = i_mutex_new();
+}
+
 static int save_tiff_tags(TIFF *tif, i_img *im);
 
 static void 
@@ -225,8 +299,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 +334,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 +367,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 +386,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 +405,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 +459,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 +468,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 +477,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 +499,6 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
       getterf = strip_contig_getter;
   }
   if (setupf && getterf && putterf) {
-    i_img_dim 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;
 
     if (!setupf(&state))
       return NULL;
@@ -462,10 +586,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 +621,68 @@ 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_mutex_lock(mutex);
 
   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);
+    i_mutex_unlock(mutex);
     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);
+      i_mutex_unlock(mutex);
       return NULL;
     }
   }
@@ -535,7 +692,13 @@ 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);
+    i_mutex_unlock(mutex);
+
   return im;
 }
 
@@ -551,37 +714,54 @@ 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_mutex_lock(mutex);
 
   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 */
 
-  mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig));
+  mm_log((1, "i_readtiff_wiol(ig %p)\n", 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);
+    i_mutex_unlock(mutex);
     return NULL;
   }
 
@@ -607,11 +787,17 @@ 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);
+  i_mutex_unlock(mutex);
+
   return results;
 }
 
@@ -1274,6 +1460,9 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
   int i;
+  tiffio_context_t ctx;
+
+  i_mutex_lock(mutex);
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
@@ -1281,18 +1470,18 @@ i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
   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);
   
 
 
@@ -1300,6 +1489,8 @@ 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);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1307,6 +1498,8 @@ 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);
+      i_mutex_unlock(mutex);
       return 0;
     }
 
@@ -1314,12 +1507,20 @@ 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);
+      i_mutex_unlock(mutex);
       return 0;
     }
   }
 
   TIFFSetErrorHandler(old_handler);
   (void) TIFFClose(tif);
+  tiffio_context_final(&ctx);
+
+  i_mutex_unlock(mutex);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1342,6 +1543,9 @@ 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;
+
+  i_mutex_lock(mutex);
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
@@ -1349,18 +1553,18 @@ i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
   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);
   
 
 
@@ -1368,6 +1572,8 @@ 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);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
@@ -1375,6 +1581,8 @@ 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);
+      i_mutex_unlock(mutex);
       return 0;
     }
 
@@ -1382,12 +1590,20 @@ 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);
+      i_mutex_unlock(mutex);
       return 0;
     }
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+  tiffio_context_final(&ctx);
+
+  i_mutex_unlock(mutex);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1406,42 +1622,54 @@ undef_int
 i_writetiff_wiol(i_img *img, io_glue *ig) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
+  tiffio_context_t ctx;
+
+  i_mutex_lock(mutex);
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
   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);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   if (!i_writetiff_low(tif, img)) {
     TIFFClose(tif);
+    tiffio_context_final(&ctx);
     TIFFSetErrorHandler(old_handler);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+  tiffio_context_final(&ctx);
+  i_mutex_unlock(mutex);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1467,24 +1695,27 @@ undef_int
 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
   TIFF* tif;
   TIFFErrorHandler old_handler;
+  tiffio_context_t ctx;
+
+  i_mutex_lock(mutex);
 
   old_handler = TIFFSetErrorHandler(error_handler);
 
   i_clear_error();
   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);
   
 
 
@@ -1492,17 +1723,26 @@ 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);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   if (!i_writetiff_low_faxable(tif, im, fine)) {
     TIFFClose(tif);
     TIFFSetErrorHandler(old_handler);
+    tiffio_context_final(&ctx);
+    i_mutex_unlock(mutex);
     return 0;
   }
 
   (void) TIFFClose(tif);
   TIFFSetErrorHandler(old_handler);
+  tiffio_context_final(&ctx);
+  i_mutex_unlock(mutex);
+
+  if (i_io_close(ig))
+    return 0;
 
   return 1;
 }
@@ -1520,7 +1760,7 @@ static int save_tiff_tags(TIFF *tif, i_img *im) {
       }
     }
   }
+
   return 1;
 }
 
@@ -1549,10 +1789,19 @@ static void pack_4bit_to(unsigned char *dest, const unsigned char *src,
   }
 }
 
-static i_img *
-make_rgb(TIFF *tif, i_img_dim width, i_img_dim 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;
 
@@ -1561,7 +1810,7 @@ make_rgb(TIFF *tif, i_img_dim width, i_img_dim height, int *alpha_chan) {
 
   switch (photometric) {
   case PHOTOMETRIC_SEPARATED:
-    channels = 3;
+    *channels = 3;
     break;
   
   case PHOTOMETRIC_MINISWHITE:
@@ -1569,11 +1818,11 @@ make_rgb(TIFF *tif, i_img_dim width, i_img_dim 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
@@ -1582,8 +1831,15 @@ make_rgb(TIFF *tif, i_img_dim width, i_img_dim 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);
 }
@@ -2469,7 +2725,8 @@ putter_cmyk16(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_
   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));
+  mm_log((4, "putter_cmyk16(%p, %" i_DF ", %" i_DF ", %" i_DF
+         ", %" i_DF ", %d)\n", state, x, y, width, height, row_extras));
 
   state->pixels_read += width * height;
   while (height > 0) {
@@ -2555,6 +2812,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