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