]> git.imager.perl.org - imager.git/blobdiff - TIFF/imtiff.c
1.005 release
[imager.git] / TIFF / imtiff.c
index 832f38d41e1ef88b58fcc277134fd1c99d2c3683..4bfabcbc44a39bc9d4c294497061f27f46b656ee 100644 (file)
@@ -87,6 +87,18 @@ compress_values[] =
 static const int compress_value_count = 
   sizeof(compress_values) / sizeof(*compress_values);
 
+static struct tag_name
+sample_format_values[] =
+  {
+    { "uint",      SAMPLEFORMAT_UINT },
+    { "int",       SAMPLEFORMAT_INT },
+    { "ieeefp",    SAMPLEFORMAT_IEEEFP },
+    { "undefined", SAMPLEFORMAT_VOID },
+  };
+
+static const int sample_format_value_count = 
+  sizeof(sample_format_values) / sizeof(*sample_format_values);
+
 static int 
 myTIFFIsCODECConfigured(uint16 scheme);
 
@@ -130,6 +142,14 @@ struct read_state_tag {
      we use is EXTRASAMPLE_ASSOCALPHA then the color data will need to
      be scaled to match Imager's conventions */
   int scale_alpha;
+
+  /* number of color samples (not including alpha) */
+  int color_channels;
+
+  /* SampleFormat is 2 */
+  int sample_signed;
+
+  int sample_format;
 };
 
 static int tile_contig_getter(read_state_t *state, read_putter_t putter);
@@ -171,12 +191,69 @@ fallback_rgb_channels(TIFF *tif, i_img_dim width, i_img_dim height, int *channel
 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;
 
@@ -207,6 +284,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 
@@ -233,7 +319,7 @@ 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;
+  io_glue *ig = ((tiffio_context_t *)h)->ig;
   return (toff_t) i_io_seek(ig, o, w);
 }
 
@@ -270,24 +356,24 @@ comp_munmap(thandle_t h, tdata_t p, toff_t off) {
 
 static tsize_t
 comp_read(thandle_t h, tdata_t p, tsize_t size) {
-  return i_io_read((io_glue *)h, p, 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((io_glue *)h, p, size);
+  return i_io_write(((tiffio_context_t *)h)->ig, p, size);
 }
 
 static int
 comp_close(thandle_t h) {
-  return i_io_close((io_glue *)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;
   uint16 samples_per_pixel;
-  int tiled, error;
+  int tiled;
   float xres, yres;
   uint16 resunit;
   int gotXres, gotYres;
@@ -296,6 +382,7 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   uint16 planar_config;
   uint16 inkset;
   uint16 compress;
+  uint16 sample_format;
   int i;
   read_state_t state;
   read_setup_t setupf = NULL;
@@ -304,8 +391,7 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   int channels = MAXCHANNELS;
   size_t sample_size = ~0; /* force failure if some code doesn't set it */
   i_img_dim total_pixels;
-
-  error = 0;
+  int samples_integral;
 
   TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
   TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
@@ -316,6 +402,13 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
   TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset);
 
+  if (samples_per_pixel == 0) {
+    i_push_error(0, "invalid image: SamplesPerPixel is 0");
+    return NULL;
+  }
+
+  TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLEFORMAT, &sample_format);
+
   mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, samples_per_pixel));
   mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
   mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
@@ -329,9 +422,16 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   state.bits_per_sample = bits_per_sample;
   state.samples_per_pixel = samples_per_pixel;
   state.photometric = photometric;
+  state.sample_signed = sample_format == SAMPLEFORMAT_INT;
+  state.sample_format = sample_format;
+
+  samples_integral = sample_format == SAMPLEFORMAT_UINT
+    || sample_format == SAMPLEFORMAT_INT 
+    || sample_format == SAMPLEFORMAT_VOID;  /* sample as UINT */
 
   /* yes, this if() is horrible */
-  if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
+  if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8
+      && samples_integral) {
     setupf = setup_paletted;
     if (bits_per_sample == 8)
       putterf = paletted_putter8;
@@ -345,28 +445,32 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   }
   else if (bits_per_sample == 16 
           && photometric == PHOTOMETRIC_RGB
-          && samples_per_pixel >= 3) {
+          && samples_per_pixel >= 3
+          && samples_integral) {
     setupf = setup_16_rgb;
     putterf = putter_16;
     sample_size = 2;
     rgb_channels(&state, &channels);
   }
   else if (bits_per_sample == 16
-          && photometric == PHOTOMETRIC_MINISBLACK) {
+          && photometric == PHOTOMETRIC_MINISBLACK
+          && samples_integral) {
     setupf = setup_16_grey;
     putterf = putter_16;
     sample_size = 2;
     grey_channels(&state, &channels);
   }
   else if (bits_per_sample == 8
-          && photometric == PHOTOMETRIC_MINISBLACK) {
+          && photometric == PHOTOMETRIC_MINISBLACK
+          && samples_integral) {
     setupf = setup_8_grey;
     putterf = putter_8;
     sample_size = 1;
     grey_channels(&state, &channels);
   }
   else if (bits_per_sample == 8
-          && photometric == PHOTOMETRIC_RGB) {
+          && photometric == PHOTOMETRIC_RGB
+          && samples_integral) {
     setupf = setup_8_rgb;
     putterf = putter_8;
     sample_size = 1;
@@ -399,7 +503,8 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   else if (bits_per_sample == 8
           && photometric == PHOTOMETRIC_SEPARATED
           && inkset == INKSET_CMYK
-          && samples_per_pixel >= 4) {
+          && samples_per_pixel >= 4
+          && samples_integral) {
     setupf = setup_cmyk8;
     putterf = putter_cmyk8;
     sample_size = 1;
@@ -408,7 +513,8 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
   else if (bits_per_sample == 16
           && photometric == PHOTOMETRIC_SEPARATED
           && inkset == INKSET_CMYK
-          && samples_per_pixel >= 4) {
+          && samples_per_pixel >= 4
+          && samples_integral) {
     setupf = setup_cmyk16;
     putterf = putter_cmyk16;
     sample_size = 2;
@@ -520,10 +626,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) {
@@ -531,12 +647,25 @@ static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) {
       break;
     }
   }
-  
+
+  if (TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sample_format)) {
+    /* only set the tag if the the TIFF tag is present */
+    i_tags_setn(&im->tags, "tiff_sample_format", sample_format);
+
+    for (i = 0; i < sample_format_value_count; ++i) {
+      if (sample_format_values[i].tag == sample_format) {
+       i_tags_set(&im->tags, "tiff_sample_format_name",
+                  sample_format_values[i].name, -1);
+       break;
+      }
+    }
+  }
+
   return im;
 }
 
 /*
-=item i_readtiff_wiol(im, ig)
+=item i_readtiff_wiol(ig, allow_incomplete, page)
 
 =cut
 */
@@ -545,23 +674,35 @@ 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,
+                      (thandle_t) &ctx,
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -575,6 +716,11 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) {
     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;
   }
 
@@ -584,7 +730,12 @@ i_readtiff_wiol(io_glue *ig, int allow_incomplete, int 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;
     }
   }
@@ -594,7 +745,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;
 }
 
@@ -610,23 +767,36 @@ 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;
+  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,
+                      (thandle_t) &ctx,
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -640,6 +810,11 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
     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;
   }
 
@@ -669,7 +844,13 @@ i_readtiff_multi_wiol(io_glue *ig, int *count) {
 
   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;
 }
 
@@ -739,7 +920,8 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
 
   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
-  mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
+  mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%lu\n",
+         (unsigned long)TIFFScanlineSize(tif) ));
   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
 
   if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
@@ -1332,6 +1514,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);
 
@@ -1339,11 +1524,11 @@ 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
+                      (thandle_t) &ctx
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -1358,6 +1543,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;
   }
 
@@ -1365,6 +1552,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;
     }
 
@@ -1372,12 +1561,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);
+      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;
@@ -1403,6 +1597,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);
 
@@ -1410,11 +1607,11 @@ 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
+                      (thandle_t) &ctx
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -1429,6 +1626,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;
   }
 
@@ -1436,6 +1635,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;
     }
 
@@ -1443,12 +1644,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);
+      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;
@@ -1470,17 +1676,20 @@ 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
+                      (thandle_t) &ctx
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -1494,18 +1703,24 @@ i_writetiff_wiol(i_img *img, io_glue *ig) {
   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;
@@ -1534,17 +1749,20 @@ 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
+                      (thandle_t) &ctx
                       comp_read,
                       comp_write,
                       comp_seek,
@@ -1559,17 +1777,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);
+    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;
@@ -1590,7 +1814,7 @@ static int save_tiff_tags(TIFF *tif, i_img *im) {
       }
     }
   }
+
   return 1;
 }
 
@@ -2108,6 +2332,7 @@ rgb_channels(read_state_t *state, int *out_channels) {
   *out_channels = 3;
   state->alpha_chan = 0;
   state->scale_alpha = 0;
+  state->color_channels = 3;
 
   /* plain RGB */
   if (state->samples_per_pixel == 3)
@@ -2153,6 +2378,7 @@ grey_channels(read_state_t *state, int *out_channels) {
   *out_channels = 1;
   state->alpha_chan = 0;
   state->scale_alpha = 0;
+  state->color_channels = 1;
 
   /* plain grey */
   if (state->samples_per_pixel == 1)
@@ -2232,6 +2458,10 @@ putter_16(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_
       for (ch = 0; ch < out_chan; ++ch) {
        outp[ch] = p[ch];
       }
+      if (state->sample_signed) {
+       for (ch = 0; ch < state->color_channels; ++ch)
+         outp[ch] ^= 0x8000;
+      }
       if (state->alpha_chan && state->scale_alpha && outp[state->alpha_chan]) {
        for (ch = 0; ch < state->alpha_chan; ++ch) {
          int result = 0.5 + (outp[ch] * 65535.0 / outp[state->alpha_chan]);
@@ -2296,6 +2526,10 @@ putter_8(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_d
       for (ch = 0; ch < out_chan; ++ch) {
        outp->channel[ch] = p[ch];
       }
+      if (state->sample_signed) {
+       for (ch = 0; ch < state->color_channels; ++ch)
+         outp->channel[ch] ^= 0x80;
+      }
       if (state->alpha_chan && state->scale_alpha 
          && outp->channel[state->alpha_chan]) {
        for (ch = 0; ch < state->alpha_chan; ++ch) {
@@ -2359,9 +2593,25 @@ putter_32(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_img_
     i_fcolor *outp = state->line_buf;
 
     for (i = 0; i < width; ++i) {
-      for (ch = 0; ch < out_chan; ++ch) {
-       outp->channel[ch] = p[ch] / 4294967295.0;
+#ifdef IEEEFP_TYPES
+      if (state->sample_format == SAMPLEFORMAT_IEEEFP) {
+       const float *pv = (const float *)p;
+       for (ch = 0; ch < out_chan; ++ch) {
+         outp->channel[ch] = pv[ch];
+       }
       }
+      else {
+#endif
+       for (ch = 0; ch < out_chan; ++ch) {
+         if (state->sample_signed && ch < state->color_channels)
+           outp->channel[ch] = (p[ch] ^ 0x80000000UL) / 4294967295.0;
+         else
+           outp->channel[ch] = p[ch] / 4294967295.0;
+       }
+#ifdef IEEEFP_TYPES
+      }
+#endif
+
       if (state->alpha_chan && state->scale_alpha && outp->channel[state->alpha_chan]) {
        for (ch = 0; ch < state->alpha_chan; ++ch)
          outp->channel[ch] /= outp->channel[state->alpha_chan];
@@ -2446,6 +2696,7 @@ cmyk_channels(read_state_t *state, int *out_channels) {
   *out_channels = 3;
   state->alpha_chan = 0;
   state->scale_alpha = 0;
+  state->color_channels = 3;
 
   /* plain CMYK */
   if (state->samples_per_pixel == 4)
@@ -2510,6 +2761,12 @@ putter_cmyk8(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_i
       m = p[1];
       y = p[2];
       k = 255 - p[3];
+      if (state->sample_signed) {
+       c ^= 0x80;
+       m ^= 0x80;
+       y ^= 0x80;
+       k ^= 0x80;
+      }
       outp->rgba.r = (k * (255 - c)) / 255;
       outp->rgba.g = (k * (255 - m)) / 255;
       outp->rgba.b = (k * (255 - y)) / 255;
@@ -2555,7 +2812,9 @@ 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, i_DFcp(x, y), i_DFcp(width, height),
+         row_extras));
 
   state->pixels_read += width * height;
   while (height > 0) {
@@ -2569,6 +2828,12 @@ putter_cmyk16(read_state_t *state, i_img_dim x, i_img_dim y, i_img_dim width, i_
       m = p[1];
       y = p[2];
       k = 65535 - p[3];
+      if (state->sample_signed) {
+       c ^= 0x8000;
+       m ^= 0x8000;
+       y ^= 0x8000;
+       k ^= 0x8000;
+      }
       outp[0] = (k * (65535U - c)) / 65535U;
       outp[1] = (k * (65535U - m)) / 65535U;
       outp[2] = (k * (65535U - y)) / 65535U;
@@ -2641,6 +2906,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