]> git.imager.perl.org - imager.git/blob - tiff.c
1485a199c5a3cfa0c2931b25b7f3b2033d9e6467
[imager.git] / tiff.c
1 #include "image.h"
2 #include "tiffio.h"
3 #include "iolayer.h"
4 #include "imagei.h"
5
6 /*
7 =head1 NAME
8
9 tiff.c - implements reading and writing tiff files, uses io layer.
10
11 =head1 SYNOPSIS
12
13    io_glue *ig = io_new_fd( fd );
14    i_img *im   = i_readtiff_wiol(ig, -1); // no limit on how much is read
15    // or 
16    io_glue *ig = io_new_fd( fd );
17    return_code = i_writetiff_wiol(im, ig); 
18
19 =head1 DESCRIPTION
20
21 tiff.c implements the basic functions to read and write tiff files.
22 It uses the iolayer and needs either a seekable source or an entire
23 memory mapped buffer.
24
25 =head1 FUNCTION REFERENCE
26
27 Some of these functions are internal.
28
29 =over
30
31 =cut
32 */
33
34
35 #define byteswap_macro(x) \
36      ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) |     \
37       (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
38
39 struct tag_name {
40   char *name;
41   uint32 tag;
42 };
43
44 static struct tag_name text_tag_names[] =
45 {
46   { "tiff_documentname", TIFFTAG_DOCUMENTNAME, },
47   { "tiff_imagedescription", TIFFTAG_IMAGEDESCRIPTION, },
48   { "tiff_make", TIFFTAG_MAKE, },
49   { "tiff_model", TIFFTAG_MODEL, },
50   { "tiff_pagename", TIFFTAG_PAGENAME, },
51   { "tiff_software", TIFFTAG_SOFTWARE, },
52   { "tiff_datetime", TIFFTAG_DATETIME, },
53   { "tiff_artist", TIFFTAG_ARTIST, },
54   { "tiff_hostcomputer", TIFFTAG_HOSTCOMPUTER, },
55 };
56
57 static const int text_tag_count = 
58   sizeof(text_tag_names) / sizeof(*text_tag_names);
59
60 static void error_handler(char const *module, char const *fmt, va_list ap) {
61   i_push_errorvf(0, fmt, ap);
62 }
63
64 static int save_tiff_tags(TIFF *tif, i_img *im);
65
66 static void expand_4bit_hl(unsigned char *buf, int count);
67
68 static void pack_4bit_hl(unsigned char *buf, int count);
69
70 /*
71 =item comp_seek(h, o, w)
72
73 Compatability for 64 bit systems like latest freebsd (internal)
74
75    h - tiff handle, cast an io_glue object
76    o - offset
77    w - whence
78
79 =cut
80 */
81
82 static
83 toff_t
84 comp_seek(thandle_t h, toff_t o, int w) {
85   io_glue *ig = (io_glue*)h;
86   return (toff_t) ig->seekcb(ig, o, w);
87 }
88
89
90 /*
91 =item i_readtiff_wiol(ig, length)
92
93 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
94
95    ig     - io_glue object
96    length - maximum length to read from data source, before closing it
97
98 =cut
99 */
100
101 i_img*
102 i_readtiff_wiol(io_glue *ig, int length) {
103   i_img *im;
104   uint32 width, height;
105   uint16 channels;
106   uint32* raster = NULL;
107   int tiled, error;
108   TIFF* tif;
109   float xres, yres;
110   uint16 resunit;
111   int gotXres, gotYres;
112   uint16 photometric;
113   uint16 bits_per_sample;
114   int i;
115   int ch;
116   TIFFErrorHandler old_handler;
117
118   i_clear_error();
119   old_handler = TIFFSetErrorHandler(error_handler);
120
121   error = 0;
122
123   /* Add code to get the filename info from the iolayer */
124   /* Also add code to check for mmapped code */
125
126   io_glue_commit_types(ig);
127   mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
128   
129   tif = TIFFClientOpen("(Iolayer)", 
130                        "rm", 
131                        (thandle_t) ig,
132                        (TIFFReadWriteProc) ig->readcb,
133                        (TIFFReadWriteProc) ig->writecb,
134                        (TIFFSeekProc) comp_seek,
135                        (TIFFCloseProc) ig->closecb,
136                        (TIFFSizeProc) ig->sizecb,
137                        (TIFFMapFileProc) NULL,
138                        (TIFFUnmapFileProc) NULL);
139   
140   if (!tif) {
141     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
142     i_push_error(0, "opening file");
143     TIFFSetErrorHandler(old_handler);
144     return NULL;
145   }
146
147   TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
148   TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
149   TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
150   tiled = TIFFIsTiled(tif);
151   TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
152   TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
153
154   mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
155   mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
156   mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
157
158   if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
159     channels = 3;
160     im = i_img_pal_new(width, height, channels, 256);
161   }
162   else {
163     im = i_img_empty_ch(NULL, width, height, channels);
164   }
165     
166   /* resolution tags */
167   TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
168   gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
169   gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
170   if (gotXres || gotYres) {
171     if (!gotXres)
172       xres = yres;
173     else if (!gotYres)
174       yres = xres;
175     if (resunit == RESUNIT_CENTIMETER) {
176       /* from dots per cm to dpi */
177       xres *= 2.54;
178       yres *= 2.54;
179     }
180     i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
181     if (resunit == RESUNIT_NONE)
182       i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
183     i_tags_set_float(&im->tags, "i_xres", 0, xres);
184     i_tags_set_float(&im->tags, "i_yres", 0, yres);
185   }
186
187   /* Text tags */
188   for (i = 0; i < text_tag_count; ++i) {
189     char *data;
190     if (TIFFGetField(tif, text_tag_names[i].tag, &data)) {
191       mm_log((1, "i_readtiff_wiol: tag %d has value %s\n", 
192               text_tag_names[i].tag, data));
193       i_tags_add(&im->tags, text_tag_names[i].name, 0, data, 
194                  strlen(data), 0);
195     }
196   }
197   
198   /*   TIFFPrintDirectory(tif, stdout, 0); good for debugging */
199
200   if (photometric == PHOTOMETRIC_PALETTE &&
201       (bits_per_sample == 4 || bits_per_sample == 8)) {
202     uint16 *maps[3];
203     char used[256];
204     int maxused;
205     uint32 row, col;
206     unsigned char *buffer;
207
208     if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
209       i_push_error(0, "Cannot get colormap for paletted image");
210       TIFFSetErrorHandler(old_handler);
211       i_img_destroy(im);
212       TIFFClose(tif);
213       return NULL;
214     }
215     buffer = (unsigned char *)_TIFFmalloc(width+2);
216     if (!buffer) {
217       i_push_error(0, "out of memory");
218       TIFFSetErrorHandler(old_handler);
219       i_img_destroy(im);
220       TIFFClose(tif);
221       return NULL;
222     }
223     row = 0;
224     memset(used, 0, sizeof(used));
225     while (row < height && TIFFReadScanline(tif, buffer, row, 0) > 0) {
226       if (bits_per_sample == 4)
227         expand_4bit_hl(buffer, (width+1)/2);
228       for (col = 0; col < width; ++col) {
229         used[buffer[col]] = 1;
230       }
231       i_ppal(im, 0, width, row, buffer);
232       ++row;
233     }
234     if (row < height) {
235       error = 1;
236     }
237     /* Ideally we'd optimize the palette, but that could be expensive
238        since we'd have to re-index every pixel.
239
240        Optimizing the palette (even at this level) might not 
241        be what the user wants, so I don't do it.
242
243        We'll add a function to optimize a paletted image instead.
244     */
245     maxused = (1 << bits_per_sample)-1;
246     if (!error) {
247       while (maxused >= 0 && !used[maxused])
248         --maxused;
249     }
250     for (i = 0; i < 1 << bits_per_sample; ++i) {
251       i_color c;
252       for (ch = 0; ch < 3; ++ch) {
253         c.channel[ch] = Sample16To8(maps[ch][i]);
254       }
255       i_addcolors(im, &c, 1);
256     }
257     _TIFFfree(buffer);
258   }
259   else {
260     if (tiled) {
261       int ok = 1;
262       uint32 row, col;
263       uint32 tile_width, tile_height;
264       
265       TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
266       TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
267       mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
268       
269       raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
270       if (!raster) {
271         i_img_destroy(im);
272         i_push_error(0, "No space for raster buffer");
273         TIFFSetErrorHandler(old_handler);
274         TIFFClose(tif);
275         return NULL;
276       }
277       
278       for( row = 0; row < height; row += tile_height ) {
279         for( col = 0; ok && col < width; col += tile_width ) {
280           uint32 i_row, x, newrows, newcols;
281           
282           /* Read the tile into an RGBA array */
283           if (!TIFFReadRGBATile(tif, col, row, raster)) {
284             ok = 0;
285             break;
286           }
287           newrows = (row+tile_height > height) ? height-row : tile_height;
288           mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
289           newcols = (col+tile_width  > width ) ? width-row  : tile_width;
290           for( i_row = 0; i_row < tile_height; i_row++ ) {
291             for(x = 0; x < newcols; x++) {
292               i_color val;
293               uint32 temp = raster[x+tile_width*(tile_height-i_row-1)];
294               val.rgba.r = TIFFGetR(temp);
295               val.rgba.g = TIFFGetG(temp);
296               val.rgba.b = TIFFGetB(temp);
297               val.rgba.a = TIFFGetA(temp);
298               i_ppix(im, col+x, row+i_row, &val);
299             }
300           }
301         }
302       }
303     } else {
304       uint32 rowsperstrip, row;
305       TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
306       mm_log((1, "i_readtiff_wiol: rowsperstrip=%d\n", rowsperstrip));
307       
308       raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
309       if (!raster) {
310         i_img_destroy(im);
311         i_push_error(0, "No space for raster buffer");
312         TIFFSetErrorHandler(old_handler);
313         TIFFClose(tif);
314         return NULL;
315       }
316       
317       for( row = 0; row < height; row += rowsperstrip ) {
318         uint32 newrows, i_row;
319         
320         if (!TIFFReadRGBAStrip(tif, row, raster)) {
321           error++;
322           break;
323         }
324         
325         newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
326         mm_log((1, "newrows=%d\n", newrows));
327         
328         for( i_row = 0; i_row < newrows; i_row++ ) { 
329           uint32 x;
330           for(x = 0; x<width; x++) {
331             i_color val;
332             uint32 temp = raster[x+width*(newrows-i_row-1)];
333             val.rgba.r = TIFFGetR(temp);
334             val.rgba.g = TIFFGetG(temp);
335             val.rgba.b = TIFFGetB(temp);
336             val.rgba.a = TIFFGetA(temp);
337             i_ppix(im, x, i_row+row, &val);
338           }
339         }
340       }
341     }
342   }
343   if (error) {
344     mm_log((1, "i_readtiff_wiol: error during reading\n"));
345     i_tags_addn(&im->tags, "i_incomplete", 0, 1);
346   }
347   if (raster)
348     _TIFFfree( raster );
349   if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
350   TIFFSetErrorHandler(old_handler);
351   TIFFClose(tif);
352   return im;
353 }
354
355
356
357 /*
358 =item i_writetif_wiol(im, ig)
359
360 Stores an image in the iolayer object.
361
362    im - image object to write out
363    ig - io_object that defines source to write to 
364
365 =cut 
366 */
367
368 /* FIXME: Add an options array in here soonish */
369
370 undef_int
371 i_writetiff_wiol(i_img *im, io_glue *ig) {
372   uint32 width, height;
373   uint16 channels;
374   uint16 predictor = 0;
375   int quality = 75;
376   int jpegcolormode = JPEGCOLORMODE_RGB;
377   uint16 compression = COMPRESSION_PACKBITS;
378   i_color val;
379   uint16 photometric;
380   uint32 rowsperstrip = (uint32) -1;  /* Let library pick default */
381   unsigned char *linebuf = NULL;
382   uint32 y;
383   tsize_t linebytes;
384   int ch, ci, rc;
385   uint32 x;
386   TIFF* tif;
387   int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
388   double xres, yres;
389   uint16 bitspersample = 8;
390   uint16 samplesperpixel;
391   uint16 *colors = NULL;
392
393   char *cc = mymalloc( 123 );
394   myfree(cc);
395
396
397   width    = im->xsize;
398   height   = im->ysize;
399   channels = im->channels;
400
401   switch (channels) {
402   case 1:
403     photometric = PHOTOMETRIC_MINISBLACK;
404     break;
405   case 3:
406     photometric = PHOTOMETRIC_RGB;
407     if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
408     else if (im->type == i_palette_type) {
409       photometric = PHOTOMETRIC_PALETTE;
410     }
411     break;
412   default:
413     /* This means a colorspace we don't handle yet */
414     mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
415     return 0;
416   }
417
418   /* Add code to get the filename info from the iolayer */
419   /* Also add code to check for mmapped code */
420
421   io_glue_commit_types(ig);
422   mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));
423
424   /* FIXME: Enable the mmap interface */
425   
426   tif = TIFFClientOpen("No name", 
427                        "wm",
428                        (thandle_t) ig, 
429                        (TIFFReadWriteProc) ig->readcb,
430                        (TIFFReadWriteProc) ig->writecb,
431                        (TIFFSeekProc)      comp_seek,
432                        (TIFFCloseProc)     ig->closecb, 
433                        (TIFFSizeProc)      ig->sizecb,
434                        (TIFFMapFileProc)   NULL,
435                        (TIFFUnmapFileProc) NULL);
436   
437
438
439   if (!tif) {
440     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
441     return 0;
442   }
443
444   mm_log((1, "i_writetiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
445   
446   if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,      width)   ) { mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); return 0; }
447   if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH,     height)  ) { mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); return 0; }
448   if (!TIFFSetField(tif, TIFFTAG_ORIENTATION,  ORIENTATION_TOPLEFT)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); return 0; }
449   if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); return 0; }
450   if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,   photometric)) { mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); return 0; }
451   if (!TIFFSetField(tif, TIFFTAG_COMPRESSION,   compression)) { mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); return 0; }
452   samplesperpixel = channels;
453   if (photometric == PHOTOMETRIC_PALETTE) {
454     uint16 *out[3];
455     i_color c;
456     int count = i_colorcount(im);
457     int size;
458     int bits;
459     int ch, i;
460     
461     samplesperpixel = 1;
462     if (count > 16)
463       bitspersample = 8;
464     else
465       bitspersample = 4;
466     size = 1 << bitspersample;
467     colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size);
468     out[0] = colors;
469     out[1] = colors + size;
470     out[2] = colors + 2 * size;
471     
472     for (i = 0; i < count; ++i) {
473       i_getcolors(im, i, &c, 1);
474       for (ch = 0; ch < 3; ++ch)
475         out[ch][i] = c.channel[ch] * 257;
476     }
477     for (; i < size; ++i) {
478       for (ch = 0; ch < 3; ++ch)
479         out[ch][i] = 0;
480     }
481     if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) { 
482       mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n", 
483               bitspersample)); 
484       return 0;
485     }
486     if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) {
487       mm_log((1, "i_writetiff_wiol: TIFFSetField colormap\n")); 
488       return 0;
489     }
490   }
491   else {
492     if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) { 
493       mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n", 
494               bitspersample)); 
495       return 0;
496     }
497   }
498   if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) { 
499     mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel)); 
500     return 0;
501   }
502
503   switch (compression) {
504   case COMPRESSION_JPEG:
505     mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
506     if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality)        ) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); return 0; }
507     if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); return 0; }
508     break;
509   case COMPRESSION_LZW:
510     mm_log((1, "i_writetiff_wiol: lzw compression\n"));
511     break;
512   case COMPRESSION_DEFLATE:
513     mm_log((1, "i_writetiff_wiol: deflate compression\n"));
514     if (predictor != 0) 
515       if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); return 0; }
516     break;
517   case COMPRESSION_PACKBITS:
518     mm_log((1, "i_writetiff_wiol: packbits compression\n"));
519     break;
520   default:
521     mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
522     return 0;
523   }
524   
525   linebytes = channels * width;
526   linebytes = TIFFScanlineSize(tif) > linebytes ? linebytes 
527     : TIFFScanlineSize(tif);
528   /* working space for the scanlines - we go from 8-bit/pixel to 4 */
529   if (photometric == PHOTOMETRIC_PALETTE && bitspersample == 4)
530     linebytes += linebytes + 1;
531   linebuf = (unsigned char *)_TIFFmalloc(linebytes);
532   
533   if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
534     mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
535
536   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
537   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
538
539   mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
540   mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
541   mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
542   mm_log((1, "i_writetiff_wiol: bitspersample = %d\n", bitspersample));
543
544   got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
545   got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
546   if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
547     aspect_only = 0;
548   if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit))
549     resunit = RESUNIT_INCH;
550   if (got_xres || got_yres) {
551     if (!got_xres)
552       xres = yres;
553     else if (!got_yres)
554       yres = xres;
555     if (aspect_only) {
556       resunit = RESUNIT_NONE;
557     }
558     else {
559       if (resunit == RESUNIT_CENTIMETER) {
560         xres /= 2.54;
561         yres /= 2.54;
562       }
563       else {
564         resunit  = RESUNIT_INCH;
565       }
566     }
567     if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
568       TIFFClose(tif);
569       i_img_destroy(im);
570       i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
571       return 0;
572     }
573     if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
574       TIFFClose(tif);
575       i_img_destroy(im);
576       i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
577       return 0;
578     }
579     if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
580       TIFFClose(tif);
581       i_img_destroy(im);
582       i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
583       return 0;
584     }
585   }
586
587   if (!save_tiff_tags(tif, im)) {
588     TIFFClose(tif);
589     return 0;
590   }
591
592   if (photometric == PHOTOMETRIC_PALETTE) {
593     for (y = 0; y < height; ++y) {
594       i_gpal(im, 0, width, y, linebuf);
595       if (bitspersample == 4)
596         pack_4bit_hl(linebuf, width);
597       if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
598         mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
599         break;
600       }
601     }
602   }
603   else {
604     for (y=0; y<height; y++) {
605       ci = 0;
606       for(x=0; x<width; x++) { 
607         (void) i_gpix(im, x, y,&val);
608         for(ch=0; ch<channels; ch++) 
609           linebuf[ci++] = val.channel[ch];
610       }
611       if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
612         mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
613         break;
614       }
615     }
616   }
617   (void) TIFFClose(tif);
618   if (linebuf) _TIFFfree(linebuf);
619   if (colors) _TIFFfree(colors);
620   return 1;
621 }
622
623 /*
624 =item i_writetiff_wiol_faxable(i_img *, io_glue *)
625
626 Stores an image in the iolayer object in faxable tiff format.
627
628    im - image object to write out
629    ig - io_object that defines source to write to 
630
631 Note, this may be rewritten to use to simply be a call to a
632 lower-level function that gives more options for writing tiff at some
633 point.
634
635 =cut
636 */
637
638 undef_int
639 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
640   uint32 width, height;
641   unsigned char *linebuf = NULL;
642   uint32 y;
643   int rc;
644   uint32 x;
645   TIFF* tif;
646   int luma_mask;
647   uint32 rowsperstrip;
648   float vres = fine ? 196 : 98;
649   int luma_chan;
650
651   width    = im->xsize;
652   height   = im->ysize;
653
654   switch (im->channels) {
655   case 1:
656   case 2:
657     luma_chan = 0;
658     break;
659   case 3:
660   case 4:
661     luma_chan = 1;
662     break;
663   default:
664     /* This means a colorspace we don't handle yet */
665     mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
666     return 0;
667   }
668
669   /* Add code to get the filename info from the iolayer */
670   /* Also add code to check for mmapped code */
671
672   io_glue_commit_types(ig);
673   mm_log((1, "i_writetiff_wiol_faxable(im 0x%p, ig 0x%p)\n", im, ig));
674
675   /* FIXME: Enable the mmap interface */
676   
677   tif = TIFFClientOpen("No name", 
678                        "wm",
679                        (thandle_t) ig, 
680                        (TIFFReadWriteProc) ig->readcb,
681                        (TIFFReadWriteProc) ig->writecb,
682                        (TIFFSeekProc)      comp_seek,
683                        (TIFFCloseProc)     ig->closecb, 
684                        (TIFFSizeProc)      ig->sizecb,
685                        (TIFFMapFileProc)   NULL,
686                        (TIFFUnmapFileProc) NULL);
687
688   if (!tif) {
689     mm_log((1, "i_writetiff_wiol_faxable: Unable to open tif file for writing\n"));
690     return 0;
691   }
692
693   mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
694   
695   if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,      width)   )
696     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
697   if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH,     height)  )
698     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
699   if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
700     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
701   if (!TIFFSetField(tif, TIFFTAG_ORIENTATION,  ORIENTATION_TOPLEFT))
702     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
703   if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,   1)        )
704     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
705   if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
706     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
707   if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
708     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
709   if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
710     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
711
712   linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
713   
714   if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
715     mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
716
717   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
718   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
719
720   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
721   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
722   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
723
724   if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
725     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
726   if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
727     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
728   if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
729     mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0; 
730   }
731
732   if (!save_tiff_tags(tif, im)) {
733     TIFFClose(tif);
734     return 0;
735   }
736
737   for (y=0; y<height; y++) {
738     int linebufpos=0;
739     for(x=0; x<width; x+=8) { 
740       int bits;
741       int bitpos;
742       i_sample_t luma[8];
743       uint8 bitval = 128;
744       linebuf[linebufpos]=0;
745       bits = width-x; if(bits>8) bits=8;
746       i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
747       for(bitpos=0;bitpos<bits;bitpos++) {
748         linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
749         bitval >>= 1;
750       }
751       linebufpos++;
752     }
753     if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
754       mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
755       break;
756     }
757   }
758   (void) TIFFClose(tif);
759   if (linebuf) _TIFFfree(linebuf);
760   return 1;
761 }
762
763 static int save_tiff_tags(TIFF *tif, i_img *im) {
764   int i;
765
766   for (i = 0; i < text_tag_count; ++i) {
767     int entry;
768     if (i_tags_find(&im->tags, text_tag_names[i].name, 0, &entry)) {
769       if (!TIFFSetField(tif, text_tag_names[i].tag, 
770                         im->tags.tags[entry].data)) {
771         i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name);
772         return 0;
773       }
774     }
775   }
776
777   return 1;
778 }
779
780 /*
781 =item expand_4bit_hl(buf, count)
782
783 Expands 4-bit/entry packed data into 1 byte/entry.
784
785 buf must contain count bytes to be expanded and have 2*count bytes total 
786 space.
787
788 The data is expanded in place.
789
790 =cut
791 */
792
793 static void expand_4bit_hl(unsigned char *buf, int count) {
794   while (--count >= 0) {
795     buf[count*2+1] = buf[count] & 0xF;
796     buf[count*2] = buf[count] >> 4;
797   }
798 }
799
800 static void pack_4bit_hl(unsigned char *buf, int count) {
801   int i = 0;
802   while (i < count) {
803     buf[i/2] = (buf[i] << 4) + buf[i+1];
804     i += 2;
805   }
806 }
807
808 /*
809 =back
810
811 =head1 AUTHOR
812
813 Arnar M. Hrafnkelsson <addi@umich.edu>
814
815 =head1 SEE ALSO
816
817 Imager(3)
818
819 =cut
820 */