]> git.imager.perl.org - imager.git/blob - tiff.c
interface consistency
[imager.git] / tiff.c
1 #include "image.h"
2 #include "tiffio.h"
3 #include "iolayer.h"
4
5
6
7 /*
8 =head1 NAME
9
10 tiff.c - implements reading and writing tiff files, uses io layer.
11
12 =head1 SYNOPSIS
13
14    io_glue *ig = io_new_fd( fd );
15    i_img *im   = i_readtiff_wiol(ig, -1); // no limit on how much is read
16    // or 
17    io_glue *ig = io_new_fd( fd );
18    return_code = i_writetiff_wiol(im, ig); 
19
20 =head1 DESCRIPTION
21
22 tiff.c implements the basic functions to read and write tiff files.
23 It uses the iolayer and needs either a seekable source or an entire
24 memory mapped buffer.
25
26 =head1 FUNCTION REFERENCE
27
28 Some of these functions are internal.
29
30 =over 4
31
32 =cut
33 */
34
35
36
37
38
39 /*
40 =item comp_seek(h, o, w)
41
42 Compatability for 64 bit systems like latest freebsd (internal)
43
44    h - tiff handle, cast an io_glue object
45    o - offset
46    w - whence
47
48 =cut
49 */
50
51 static
52 toff_t
53 comp_seek(thandle_t h, toff_t o, int w) {
54   io_glue *ig = (io_glue*)h;
55   return (toff_t) ig->seekcb(ig, o, w);
56 }
57
58
59 /*
60 =item i_readtiff_wiol(ig, length)
61
62 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
63
64    ig     - io_glue object
65    length - maximum length to read from data source, before closing it
66
67 =cut
68 */
69
70 i_img*
71 i_readtiff_wiol(io_glue *ig, int length) {
72   i_img *im;
73   uint32 width, height;
74   uint16 channels;
75   uint32* raster;
76   int tiled, error;
77   TIFF* tif;
78
79   error = 0;
80
81   /* Add code to get the filename info from the iolayer */
82   /* Also add code to check for mmapped code */
83
84   io_glue_commit_types(ig);
85   mm_log((1, "i_readtiff_wiol(ig 0x%p, length %d)\n", ig, length));
86   
87   tif = TIFFClientOpen("Iolayer: FIXME", 
88                        "rm", 
89                        (thandle_t) ig,
90                        (TIFFReadWriteProc) ig->readcb,
91                        (TIFFReadWriteProc) ig->writecb,
92                        (TIFFSeekProc) comp_seek,
93                        (TIFFCloseProc) ig->closecb,
94                        (TIFFSizeProc) ig->sizecb,
95                        (TIFFMapFileProc) NULL,
96                        (TIFFUnmapFileProc) NULL);
97   
98   if (!tif) {
99     mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
100     return NULL;
101   }
102
103   TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
104   TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
105   TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
106   tiled = TIFFIsTiled(tif);
107
108   mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
109   mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
110   mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
111   
112   im = i_img_empty_ch(NULL, width, height, channels);
113   
114   /*   TIFFPrintDirectory(tif, stdout, 0); good for debugging */
115   
116   if (tiled) {
117     int ok = 1;
118     uint32 row, col;
119     uint32 tile_width, tile_height;
120
121     TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
122     TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
123     mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
124
125     raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
126     if (!raster) {
127       TIFFError(TIFFFileName(tif), "No space for raster buffer");
128       return NULL;
129     }
130     
131     for( row = 0; row < height; row += tile_height ) {
132       for( col = 0; ok && col < width; col += tile_width ) {
133         uint32 i_row, x, newrows, newcols;
134
135         /* Read the tile into an RGBA array */
136         if (!TIFFReadRGBATile(tif, col, row, raster)) {
137           ok = 0;
138           break;
139         }
140         newrows = (row+tile_height > height) ? height-row : tile_height;
141         mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
142         newcols = (col+tile_width  > width ) ? width-row  : tile_width;
143         for( i_row = 0; i_row < tile_height; i_row++ ) {
144           for(x = 0; x < newcols; x++) {
145             i_color val;               /* FIXME: Make sure this works everywhere */
146             val.ui = raster[x+tile_width*(tile_height-i_row-1)];
147             i_ppix(im, col+x, row+i_row, &val);
148           }
149         }
150       }
151     }
152   } else {
153     uint32 rowsperstrip, row;
154     TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
155     mm_log((1, "i_readtiff_wiol: rowsperstrip=%d\n", rowsperstrip));
156     
157     raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
158     if (!raster) {
159       TIFFError(TIFFFileName(tif), "No space for raster buffer");
160       return NULL;
161     }
162     
163     for( row = 0; row < height; row += rowsperstrip ) {
164       uint32 newrows, i_row;
165       
166       if (!TIFFReadRGBAStrip(tif, row, raster)) {
167         error++;
168         break;
169       }
170       
171       newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
172       mm_log((1, "newrows=%d\n", newrows));
173       
174       for( i_row = 0; i_row < newrows; i_row++ ) { 
175         uint32 x;
176         for(x = 0; x<width; x++) {
177           i_color val;               /* FIXME: Make sure this works everywhere */
178           val.ui = raster[x+width*(newrows-i_row-1)];
179           i_ppix(im, x, i_row+row, &val);
180         }
181       }
182     }
183
184   }
185   if (error) {
186     mm_log((1, "i_readtiff_wiol: error during reading\n"));
187   }
188   _TIFFfree( raster );
189   if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
190   return im;
191 }
192
193
194
195 /*
196 =item i_writetif_wiol(im, ig)
197
198 Stores an image in the iolayer object.
199
200    im - image object to write out
201    ig - io_object that defines source to write to 
202
203 =cut 
204 */
205
206
207 /* FIXME: Add an options array in here soonish */
208
209 undef_int
210 i_writetiff_wiol(i_img *im, io_glue *ig) {
211   uint32 width, height;
212   uint16 channels;
213   uint16 predictor = 0;
214   int quality = 75;
215   int jpegcolormode = JPEGCOLORMODE_RGB;
216   uint16 compression = COMPRESSION_PACKBITS;
217   i_color val;
218   uint16 photometric;
219   uint32 rowsperstrip = (uint32) -1;  /* Let library pick default */
220   double resolution = -1;
221   unsigned char *linebuf = NULL;
222   uint32 y;
223   tsize_t linebytes;
224   int ch, ci, rc;
225   uint32 x;
226   TIFF* tif;
227
228   char *cc = mymalloc( 123 );
229   myfree(cc);
230
231
232   width    = im->xsize;
233   height   = im->ysize;
234   channels = im->channels;
235
236   switch (channels) {
237   case 1:
238     photometric = PHOTOMETRIC_MINISBLACK;
239     break;
240   case 3:
241     photometric = PHOTOMETRIC_RGB;
242     if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
243     break;
244   default:
245     /* This means a colorspace we don't handle yet */
246     mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
247     return 0;
248   }
249
250   /* Add code to get the filename info from the iolayer */
251   /* Also add code to check for mmapped code */
252
253   io_glue_commit_types(ig);
254   mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));
255
256   /* FIXME: Enable the mmap interface */
257   
258   tif = TIFFClientOpen("No name", 
259                        "wm",
260                        (thandle_t) ig, 
261                        (TIFFReadWriteProc) ig->readcb,
262                        (TIFFReadWriteProc) ig->writecb,
263                        (TIFFSeekProc)      comp_seek,
264                        (TIFFCloseProc)     ig->closecb, 
265                        (TIFFSizeProc)      ig->sizecb,
266                        (TIFFMapFileProc)   NULL,
267                        (TIFFUnmapFileProc) NULL);
268   
269
270
271   if (!tif) {
272     mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
273     return 0;
274   }
275
276   mm_log((1, "i_writetiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
277   
278   if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,      width)   ) { mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); return 0; }
279   if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH,     height)  ) { mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); return 0; }
280   if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels)) { mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", channels)); return 0; }
281   if (!TIFFSetField(tif, TIFFTAG_ORIENTATION,  ORIENTATION_TOPLEFT)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); return 0; }
282   if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,   8)        ) { mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=8\n")); return 0; }
283   if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); return 0; }
284   if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,   photometric)) { mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); return 0; }
285   if (!TIFFSetField(tif, TIFFTAG_COMPRESSION,   compression)) { mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); return 0; }
286
287   switch (compression) {
288   case COMPRESSION_JPEG:
289     mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
290     if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality)        ) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); return 0; }
291     if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); return 0; }
292     break;
293   case COMPRESSION_LZW:
294     mm_log((1, "i_writetiff_wiol: lzw compression\n"));
295     break;
296   case COMPRESSION_DEFLATE:
297     mm_log((1, "i_writetiff_wiol: deflate compression\n"));
298     if (predictor != 0) 
299       if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); return 0; }
300     break;
301   case COMPRESSION_PACKBITS:
302     mm_log((1, "i_writetiff_wiol: packbits compression\n"));
303     break;
304   default:
305     mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
306     return 0;
307   }
308   
309   linebytes = channels * width;
310   linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) > linebytes ?
311                                           linebytes : TIFFScanlineSize(tif) );
312   
313   if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
314     mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
315
316   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
317   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
318
319   mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
320   mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
321   mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
322
323   if (resolution > 0) {
324     if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Xresolution=%d\n", resolution)); return 0; }
325     if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Yresolution=%d\n", resolution)); return 0; }
326     if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
327       mm_log((1, "i_writetiff_wiol: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0; 
328     }
329   }
330   
331   for (y=0; y<height; y++) {
332     ci = 0;
333     for(x=0; x<width; x++) { 
334       (void) i_gpix(im, x, y,&val);
335       for(ch=0; ch<channels; ch++) linebuf[ci++] = val.channel[ch];
336     }
337     if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
338       mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
339       break;
340     }
341   }
342   (void) TIFFClose(tif);
343   if (linebuf) _TIFFfree(linebuf);
344   return 1;
345 }
346
347 /*
348 =item i_writetiff_wiol_faxable(i_img *, io_glue *)
349
350 Stores an image in the iolayer object in faxable tiff format.
351
352    im - image object to write out
353    ig - io_object that defines source to write to 
354
355 Note, this may be rewritten to use to simply be a call to a
356 lower-level function that gives more options for writing tiff at some
357 point.
358
359 =cut
360 */
361
362 undef_int
363 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
364   uint32 width, height;
365   unsigned char *linebuf = NULL;
366   uint32 y;
367   int rc;
368   uint32 x;
369   TIFF* tif;
370   int luma_channel;
371   uint32 rowsperstrip;
372   float vres = fine ? 196 : 98;
373
374   width    = im->xsize;
375   height   = im->ysize;
376
377   switch (im->channels) {
378   case 1:
379     luma_channel = 0;
380     break;
381   case 3:
382     luma_channel = 1;
383     break;
384   default:
385     /* This means a colorspace we don't handle yet */
386     mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
387     return 0;
388   }
389
390   /* Add code to get the filename info from the iolayer */
391   /* Also add code to check for mmapped code */
392
393   io_glue_commit_types(ig);
394   mm_log((1, "i_writetiff_wiol_faxable(im 0x%p, ig 0x%p)\n", im, ig));
395
396   /* FIXME: Enable the mmap interface */
397   
398   tif = TIFFClientOpen("No name", 
399                        "wm",
400                        (thandle_t) ig, 
401                        (TIFFReadWriteProc) ig->readcb,
402                        (TIFFReadWriteProc) ig->writecb,
403                        (TIFFSeekProc)      comp_seek,
404                        (TIFFCloseProc)     ig->closecb, 
405                        (TIFFSizeProc)      ig->sizecb,
406                        (TIFFMapFileProc)   NULL,
407                        (TIFFUnmapFileProc) NULL);
408
409   if (!tif) {
410     mm_log((1, "i_writetiff_wiol_faxable: Unable to open tif file for writing\n"));
411     return 0;
412   }
413
414   mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
415   
416   if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH,      width)   )
417     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
418   if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH,     height)  )
419     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
420   if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
421     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
422   if (!TIFFSetField(tif, TIFFTAG_ORIENTATION,  ORIENTATION_TOPLEFT))
423     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
424   if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE,   1)        )
425     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
426   if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
427     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
428   if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
429     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
430   if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
431     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
432
433   linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
434   
435   if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
436     mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
437
438   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
439   TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
440
441   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
442   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
443   mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
444
445   if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
446     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
447   if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
448     { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
449   if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
450     mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0; 
451   }
452
453   for (y=0; y<height; y++) {
454     int linebufpos=0;
455     for(x=0; x<width; x+=8) { 
456       int bits;
457       int bitpos;
458       uint8 bitval = 128;
459       linebuf[linebufpos]=0;
460       bits = width-x; if(bits>8) bits=8;
461       for(bitpos=0;bitpos<bits;bitpos++) {
462         int luma;
463         luma = im->data[(x+bitpos+y*im->xsize)*im->channels+luma_channel];
464         linebuf[linebufpos] |= ((luma>=128)?bitval:0);
465         bitval >>= 1;
466       }
467       linebufpos++;
468     }
469     if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
470       mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
471       break;
472     }
473   }
474   (void) TIFFClose(tif);
475   if (linebuf) _TIFFfree(linebuf);
476   return 1;
477 }
478