8 tiff.c - implements reading and writing tiff files, uses io layer.
12 io_glue *ig = io_new_fd( fd );
13 i_img *im = i_readtiff_wiol(ig, -1); // no limit on how much is read
15 io_glue *ig = io_new_fd( fd );
16 return_code = i_writetiff_wiol(im, ig);
20 tiff.c implements the basic functions to read and write tiff files.
21 It uses the iolayer and needs either a seekable source or an entire
24 =head1 FUNCTION REFERENCE
26 Some of these functions are internal.
34 =item comp_seek(h, o, w)
36 Compatability for 64 bit systems like latest freebsd (internal)
38 h - tiff handle, cast an io_glue object
47 comp_seek(thandle_t h, toff_t o, int w) {
48 io_glue *ig = (io_glue*)h;
49 return (toff_t) ig->seekcb(ig, o, w);
54 =item i_readtiff_wiol(ig, length)
56 Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
59 length - maximum length to read from data source, before closing it
65 i_readtiff_wiol(io_glue *ig, int length) {
78 /* Add code to get the filename info from the iolayer */
79 /* Also add code to check for mmapped code */
81 io_glue_commit_types(ig);
82 mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
84 tif = TIFFClientOpen("Iolayer: FIXME",
87 (TIFFReadWriteProc) ig->readcb,
88 (TIFFReadWriteProc) ig->writecb,
89 (TIFFSeekProc) comp_seek,
90 (TIFFCloseProc) ig->closecb,
91 (TIFFSizeProc) ig->sizecb,
92 (TIFFMapFileProc) NULL,
93 (TIFFUnmapFileProc) NULL);
96 mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
100 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
101 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
102 TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
103 tiled = TIFFIsTiled(tif);
105 mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
106 mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
107 mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
109 im = i_img_empty_ch(NULL, width, height, channels);
111 if (!TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &resunit))
112 resunit = RESUNIT_INCH;
113 gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
114 gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
115 if (gotXres || gotYres) {
120 if (resunit == RESUNIT_CENTIMETER) {
121 /* from dots per cm to dpi */
125 i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
126 if (resunit == RESUNIT_NONE)
127 i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
128 i_tags_set_float(&im->tags, "i_xres", 0, xres);
129 i_tags_set_float(&im->tags, "i_yres", 0, yres);
132 /* TIFFPrintDirectory(tif, stdout, 0); good for debugging */
137 uint32 tile_width, tile_height;
139 TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
140 TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
141 mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
143 raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
145 TIFFError(TIFFFileName(tif), "No space for raster buffer");
149 for( row = 0; row < height; row += tile_height ) {
150 for( col = 0; ok && col < width; col += tile_width ) {
151 uint32 i_row, x, newrows, newcols;
153 /* Read the tile into an RGBA array */
154 if (!TIFFReadRGBATile(tif, col, row, raster)) {
158 newrows = (row+tile_height > height) ? height-row : tile_height;
159 mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
160 newcols = (col+tile_width > width ) ? width-row : tile_width;
161 for( i_row = 0; i_row < tile_height; i_row++ ) {
162 for(x = 0; x < newcols; x++) {
163 i_color val; /* FIXME: Make sure this works everywhere */
164 val.ui = raster[x+tile_width*(tile_height-i_row-1)];
165 i_ppix(im, col+x, row+i_row, &val);
171 uint32 rowsperstrip, row;
172 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
173 mm_log((1, "i_readtiff_wiol: rowsperstrip=%d\n", rowsperstrip));
175 raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
177 TIFFError(TIFFFileName(tif), "No space for raster buffer");
181 for( row = 0; row < height; row += rowsperstrip ) {
182 uint32 newrows, i_row;
184 if (!TIFFReadRGBAStrip(tif, row, raster)) {
189 newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
190 mm_log((1, "newrows=%d\n", newrows));
192 for( i_row = 0; i_row < newrows; i_row++ ) {
194 for(x = 0; x<width; x++) {
195 i_color val; /* FIXME: Make sure this works everywhere */
196 val.ui = raster[x+width*(newrows-i_row-1)];
197 i_ppix(im, x, i_row+row, &val);
204 mm_log((1, "i_readtiff_wiol: error during reading\n"));
207 if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
214 =item i_writetif_wiol(im, ig)
216 Stores an image in the iolayer object.
218 im - image object to write out
219 ig - io_object that defines source to write to
225 /* FIXME: Add an options array in here soonish */
228 i_writetiff_wiol(i_img *im, io_glue *ig) {
229 uint32 width, height;
231 uint16 predictor = 0;
233 int jpegcolormode = JPEGCOLORMODE_RGB;
234 uint16 compression = COMPRESSION_PACKBITS;
237 uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
238 unsigned char *linebuf = NULL;
244 int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
247 char *cc = mymalloc( 123 );
253 channels = im->channels;
257 photometric = PHOTOMETRIC_MINISBLACK;
260 photometric = PHOTOMETRIC_RGB;
261 if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
264 /* This means a colorspace we don't handle yet */
265 mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
269 /* Add code to get the filename info from the iolayer */
270 /* Also add code to check for mmapped code */
272 io_glue_commit_types(ig);
273 mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));
275 /* FIXME: Enable the mmap interface */
277 tif = TIFFClientOpen("No name",
280 (TIFFReadWriteProc) ig->readcb,
281 (TIFFReadWriteProc) ig->writecb,
282 (TIFFSeekProc) comp_seek,
283 (TIFFCloseProc) ig->closecb,
284 (TIFFSizeProc) ig->sizecb,
285 (TIFFMapFileProc) NULL,
286 (TIFFUnmapFileProc) NULL);
291 mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
295 mm_log((1, "i_writetiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
297 if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); return 0; }
298 if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); return 0; }
299 if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels)) { mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", channels)); return 0; }
300 if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); return 0; }
301 if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=8\n")); return 0; }
302 if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); return 0; }
303 if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) { mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); return 0; }
304 if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) { mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); return 0; }
306 switch (compression) {
307 case COMPRESSION_JPEG:
308 mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
309 if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); return 0; }
310 if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); return 0; }
312 case COMPRESSION_LZW:
313 mm_log((1, "i_writetiff_wiol: lzw compression\n"));
315 case COMPRESSION_DEFLATE:
316 mm_log((1, "i_writetiff_wiol: deflate compression\n"));
318 if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); return 0; }
320 case COMPRESSION_PACKBITS:
321 mm_log((1, "i_writetiff_wiol: packbits compression\n"));
324 mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
328 linebytes = channels * width;
329 linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) > linebytes ?
330 linebytes : TIFFScanlineSize(tif) );
332 if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
333 mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
335 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
336 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
338 mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
339 mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
340 mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
342 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
343 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
344 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
346 if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit))
347 resunit = RESUNIT_INCH;
348 if (got_xres || got_yres) {
354 resunit = RESUNIT_NONE;
357 if (resunit == RESUNIT_CENTIMETER) {
362 resunit = RESUNIT_INCH;
365 if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
368 i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
371 if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
374 i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
377 if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
380 i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
385 for (y=0; y<height; y++) {
387 for(x=0; x<width; x++) {
388 (void) i_gpix(im, x, y,&val);
389 for(ch=0; ch<channels; ch++) linebuf[ci++] = val.channel[ch];
391 if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
392 mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
396 (void) TIFFClose(tif);
397 if (linebuf) _TIFFfree(linebuf);
402 =item i_writetiff_wiol_faxable(i_img *, io_glue *)
404 Stores an image in the iolayer object in faxable tiff format.
406 im - image object to write out
407 ig - io_object that defines source to write to
409 Note, this may be rewritten to use to simply be a call to a
410 lower-level function that gives more options for writing tiff at some
417 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
418 uint32 width, height;
419 unsigned char *linebuf = NULL;
426 float vres = fine ? 196 : 98;
432 switch (im->channels) {
442 /* This means a colorspace we don't handle yet */
443 mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
447 /* Add code to get the filename info from the iolayer */
448 /* Also add code to check for mmapped code */
450 io_glue_commit_types(ig);
451 mm_log((1, "i_writetiff_wiol_faxable(im 0x%p, ig 0x%p)\n", im, ig));
453 /* FIXME: Enable the mmap interface */
455 tif = TIFFClientOpen("No name",
458 (TIFFReadWriteProc) ig->readcb,
459 (TIFFReadWriteProc) ig->writecb,
460 (TIFFSeekProc) comp_seek,
461 (TIFFCloseProc) ig->closecb,
462 (TIFFSizeProc) ig->sizecb,
463 (TIFFMapFileProc) NULL,
464 (TIFFUnmapFileProc) NULL);
467 mm_log((1, "i_writetiff_wiol_faxable: Unable to open tif file for writing\n"));
471 mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
473 if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) )
474 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
475 if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) )
476 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
477 if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
478 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
479 if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT))
480 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
481 if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1) )
482 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
483 if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
484 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
485 if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
486 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
487 if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
488 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
490 linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
492 if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
493 mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
495 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
496 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
498 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
499 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
500 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
502 if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
503 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
504 if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
505 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
506 if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
507 mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
510 for (y=0; y<height; y++) {
512 for(x=0; x<width; x+=8) {
517 linebuf[linebufpos]=0;
518 bits = width-x; if(bits>8) bits=8;
519 i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
520 for(bitpos=0;bitpos<bits;bitpos++) {
521 linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
526 if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
527 mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
531 (void) TIFFClose(tif);
532 if (linebuf) _TIFFfree(linebuf);