9 tiff.c - implements reading and writing tiff files, uses io layer.
13 io_glue *ig = io_new_fd( fd );
14 i_img *im = i_readtiff_wiol(ig, -1); // no limit on how much is read
16 io_glue *ig = io_new_fd( fd );
17 return_code = i_writetiff_wiol(im, ig);
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
25 =head1 FUNCTION REFERENCE
27 Some of these functions are internal.
35 #define byteswap_macro(x) \
36 ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
37 (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
44 static struct tag_name text_tag_names[] =
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, },
57 static const int text_tag_count =
58 sizeof(text_tag_names) / sizeof(*text_tag_names);
60 static void error_handler(char const *module, char const *fmt, va_list ap) {
61 i_push_errorvf(0, fmt, ap);
64 static void warn_handler(char const *module, char const *fmt, va_list ap) {
65 /* for now do nothing, perhaps we could warn(), though that should be
66 done in the XS code, not in the code which isn't mean to know perl
70 static int save_tiff_tags(TIFF *tif, i_img *im);
72 static void expand_4bit_hl(unsigned char *buf, int count);
74 static void pack_4bit_hl(unsigned char *buf, int count);
77 static toff_t sizeproc(thandle_t x) {
83 =item comp_seek(h, o, w)
85 Compatability for 64 bit systems like latest freebsd (internal)
87 h - tiff handle, cast an io_glue object
96 comp_seek(thandle_t h, toff_t o, int w) {
97 io_glue *ig = (io_glue*)h;
98 return (toff_t) ig->seekcb(ig, o, w);
101 static i_img *read_one_tiff(TIFF *tif) {
103 uint32 width, height;
105 uint32* raster = NULL;
109 int gotXres, gotYres;
111 uint16 bits_per_sample;
117 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
118 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
119 TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
120 tiled = TIFFIsTiled(tif);
121 TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
122 TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
124 mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
125 mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
126 mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
128 if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
130 im = i_img_pal_new(width, height, channels, 256);
133 im = i_img_empty_ch(NULL, width, height, channels);
136 /* resolution tags */
137 TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
138 gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
139 gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
140 if (gotXres || gotYres) {
145 if (resunit == RESUNIT_CENTIMETER) {
146 /* from dots per cm to dpi */
150 i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
151 if (resunit == RESUNIT_NONE)
152 i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
153 i_tags_set_float(&im->tags, "i_xres", 0, xres);
154 i_tags_set_float(&im->tags, "i_yres", 0, yres);
158 for (i = 0; i < text_tag_count; ++i) {
160 if (TIFFGetField(tif, text_tag_names[i].tag, &data)) {
161 mm_log((1, "i_readtiff_wiol: tag %d has value %s\n",
162 text_tag_names[i].tag, data));
163 i_tags_add(&im->tags, text_tag_names[i].name, 0, data,
168 /* TIFFPrintDirectory(tif, stdout, 0); good for debugging */
170 if (photometric == PHOTOMETRIC_PALETTE &&
171 (bits_per_sample == 4 || bits_per_sample == 8)) {
176 unsigned char *buffer;
178 if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
179 i_push_error(0, "Cannot get colormap for paletted image");
183 buffer = (unsigned char *)_TIFFmalloc(width+2);
185 i_push_error(0, "out of memory");
190 memset(used, 0, sizeof(used));
191 while (row < height && TIFFReadScanline(tif, buffer, row, 0) > 0) {
192 if (bits_per_sample == 4)
193 expand_4bit_hl(buffer, (width+1)/2);
194 for (col = 0; col < width; ++col) {
195 used[buffer[col]] = 1;
197 i_ppal(im, 0, width, row, buffer);
203 /* Ideally we'd optimize the palette, but that could be expensive
204 since we'd have to re-index every pixel.
206 Optimizing the palette (even at this level) might not
207 be what the user wants, so I don't do it.
209 We'll add a function to optimize a paletted image instead.
211 maxused = (1 << bits_per_sample)-1;
213 while (maxused >= 0 && !used[maxused])
216 for (i = 0; i < 1 << bits_per_sample; ++i) {
218 for (ch = 0; ch < 3; ++ch) {
219 c.channel[ch] = Sample16To8(maps[ch][i]);
221 i_addcolors(im, &c, 1);
229 uint32 tile_width, tile_height;
231 TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
232 TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
233 mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
235 raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
238 i_push_error(0, "No space for raster buffer");
242 for( row = 0; row < height; row += tile_height ) {
243 for( col = 0; ok && col < width; col += tile_width ) {
244 uint32 i_row, x, newrows, newcols;
246 /* Read the tile into an RGBA array */
247 if (!TIFFReadRGBATile(tif, col, row, raster)) {
251 newrows = (row+tile_height > height) ? height-row : tile_height;
252 mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
253 newcols = (col+tile_width > width ) ? width-row : tile_width;
254 for( i_row = 0; i_row < tile_height; i_row++ ) {
255 for(x = 0; x < newcols; x++) {
257 uint32 temp = raster[x+tile_width*(tile_height-i_row-1)];
258 val.rgba.r = TIFFGetR(temp);
259 val.rgba.g = TIFFGetG(temp);
260 val.rgba.b = TIFFGetB(temp);
261 val.rgba.a = TIFFGetA(temp);
262 i_ppix(im, col+x, row+i_row, &val);
268 uint32 rowsperstrip, row;
269 int rc = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
270 mm_log((1, "i_readtiff_wiol: rowsperstrip=%d rc = %d\n", rowsperstrip, rc));
272 if (rc != 1 || rowsperstrip==-1) {
273 rowsperstrip = height;
276 raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
279 i_push_error(0, "No space for raster buffer");
283 for( row = 0; row < height; row += rowsperstrip ) {
284 uint32 newrows, i_row;
286 if (!TIFFReadRGBAStrip(tif, row, raster)) {
291 newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
292 mm_log((1, "newrows=%d\n", newrows));
294 for( i_row = 0; i_row < newrows; i_row++ ) {
296 for(x = 0; x<width; x++) {
298 uint32 temp = raster[x+width*(newrows-i_row-1)];
299 val.rgba.r = TIFFGetR(temp);
300 val.rgba.g = TIFFGetG(temp);
301 val.rgba.b = TIFFGetB(temp);
302 val.rgba.a = TIFFGetA(temp);
303 i_ppix(im, x, i_row+row, &val);
310 mm_log((1, "i_readtiff_wiol: error during reading\n"));
311 i_tags_addn(&im->tags, "i_incomplete", 0, 1);
320 =item i_readtiff_wiol(im, ig)
325 i_readtiff_wiol(io_glue *ig, int length) {
327 TIFFErrorHandler old_handler;
328 TIFFErrorHandler old_warn_handler;
332 old_handler = TIFFSetErrorHandler(error_handler);
333 old_warn_handler = TIFFSetWarningHandler(warn_handler);
335 /* Add code to get the filename info from the iolayer */
336 /* Also add code to check for mmapped code */
338 io_glue_commit_types(ig);
339 mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
341 tif = TIFFClientOpen("(Iolayer)",
344 (TIFFReadWriteProc) ig->readcb,
345 (TIFFReadWriteProc) ig->writecb,
346 (TIFFSeekProc) comp_seek,
347 (TIFFCloseProc) ig->closecb,
348 ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
349 (TIFFMapFileProc) NULL,
350 (TIFFUnmapFileProc) NULL);
353 mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
354 i_push_error(0, "opening file");
355 TIFFSetErrorHandler(old_handler);
356 TIFFSetWarningHandler(old_warn_handler);
360 im = read_one_tiff(tif);
362 if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
363 TIFFSetErrorHandler(old_handler);
364 TIFFSetWarningHandler(old_warn_handler);
370 =item i_readtiff_multi_wiol(ig, length, *count)
372 Reads multiple images from a TIFF.
377 i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
379 TIFFErrorHandler old_handler;
380 TIFFErrorHandler old_warn_handler;
381 i_img **results = NULL;
382 int result_alloc = 0;
386 old_handler = TIFFSetErrorHandler(error_handler);
387 old_warn_handler = TIFFSetWarningHandler(warn_handler);
389 /* Add code to get the filename info from the iolayer */
390 /* Also add code to check for mmapped code */
392 io_glue_commit_types(ig);
393 mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
395 tif = TIFFClientOpen("(Iolayer)",
398 (TIFFReadWriteProc) ig->readcb,
399 (TIFFReadWriteProc) ig->writecb,
400 (TIFFSeekProc) comp_seek,
401 (TIFFCloseProc) ig->closecb,
402 (TIFFSizeProc) ig->sizecb,
403 (TIFFMapFileProc) NULL,
404 (TIFFUnmapFileProc) NULL);
407 mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
408 i_push_error(0, "opening file");
409 TIFFSetErrorHandler(old_handler);
410 TIFFSetWarningHandler(old_warn_handler);
416 i_img *im = read_one_tiff(tif);
419 if (++*count > result_alloc) {
420 if (result_alloc == 0) {
422 results = mymalloc(result_alloc * sizeof(i_img *));
427 newresults = myrealloc(results, result_alloc * sizeof(i_img *));
429 i_img_destroy(im); /* don't leak it */
432 results = newresults;
435 results[*count-1] = im;
436 } while (TIFFSetDirectory(tif, ++dirnum));
438 TIFFSetWarningHandler(old_warn_handler);
439 TIFFSetErrorHandler(old_handler);
445 i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
446 uint32 width, height;
447 unsigned char *linebuf = NULL;
453 float vres = fine ? 196 : 98;
459 switch (im->channels) {
469 /* This means a colorspace we don't handle yet */
470 mm_log((1, "i_writetiff_wiol_faxable: don't handle %d channel images.\n", im->channels));
474 /* Add code to get the filename info from the iolayer */
475 /* Also add code to check for mmapped code */
478 mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
480 if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) )
481 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField width=%d failed\n", width)); return 0; }
482 if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) )
483 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField length=%d failed\n", height)); return 0; }
484 if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1))
485 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField samplesperpixel=1 failed\n")); return 0; }
486 if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT))
487 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Orientation=topleft\n")); return 0; }
488 if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 1) )
489 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField bitpersample=1\n")); return 0; }
490 if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG))
491 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField planarconfig\n")); return 0; }
492 if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK))
493 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField photometric=%d\n", PHOTOMETRIC_MINISBLACK)); return 0; }
494 if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, 3))
495 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField compression=3\n")); return 0; }
497 linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) );
499 if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) {
500 mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField rowsperstrip=-1\n")); return 0; }
502 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
503 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
505 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
506 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
507 mm_log((1, "i_writetiff_wiol_faxable: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
509 if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)204))
510 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Xresolution=204\n")); return 0; }
511 if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, vres))
512 { mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField Yresolution=196\n")); return 0; }
513 if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
514 mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
517 if (!save_tiff_tags(tif, im)) {
521 for (y=0; y<height; y++) {
523 for(x=0; x<width; x+=8) {
528 linebuf[linebufpos]=0;
529 bits = width-x; if(bits>8) bits=8;
530 i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
531 for(bitpos=0;bitpos<bits;bitpos++) {
532 linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
537 if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
538 mm_log((1, "i_writetiff_wiol_faxable: TIFFWriteScanline failed.\n"));
542 if (linebuf) _TIFFfree(linebuf);
548 i_writetiff_low(TIFF *tif, i_img *im) {
549 uint32 width, height;
551 uint16 predictor = 0;
553 int jpegcolormode = JPEGCOLORMODE_RGB;
554 uint16 compression = COMPRESSION_PACKBITS;
557 uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
558 unsigned char *linebuf = NULL;
563 int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
565 uint16 bitspersample = 8;
566 uint16 samplesperpixel;
567 uint16 *colors = NULL;
571 channels = im->channels;
575 photometric = PHOTOMETRIC_MINISBLACK;
578 photometric = PHOTOMETRIC_RGB;
579 if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
580 else if (im->type == i_palette_type) {
581 photometric = PHOTOMETRIC_PALETTE;
585 /* This means a colorspace we don't handle yet */
586 mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
590 /* Add code to get the filename info from the iolayer */
591 /* Also add code to check for mmapped code */
593 /*io_glue_commit_types(ig);*/
594 /*mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));*/
596 mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d\n", width, height, channels));
598 if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) {
599 mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width));
602 if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) {
603 mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height));
606 if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) {
607 mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n"));
610 if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
611 mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n"));
614 if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) {
615 mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric));
618 if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) {
619 mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression));
622 samplesperpixel = channels;
623 if (photometric == PHOTOMETRIC_PALETTE) {
626 int count = i_colorcount(im);
636 size = 1 << bitspersample;
637 colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size);
639 out[1] = colors + size;
640 out[2] = colors + 2 * size;
642 for (i = 0; i < count; ++i) {
643 i_getcolors(im, i, &c, 1);
644 for (ch = 0; ch < 3; ++ch)
645 out[ch][i] = c.channel[ch] * 257;
647 for (; i < size; ++i) {
648 for (ch = 0; ch < 3; ++ch)
651 if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
652 mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
656 if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) {
657 mm_log((1, "i_writetiff_wiol: TIFFSetField colormap\n"));
662 if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
663 mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
668 if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) {
669 mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel));
673 switch (compression) {
674 case COMPRESSION_JPEG:
675 mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
676 if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) {
677 mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality));
680 if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) {
681 mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode));
685 case COMPRESSION_LZW:
686 mm_log((1, "i_writetiff_wiol: lzw compression\n"));
688 case COMPRESSION_DEFLATE:
689 mm_log((1, "i_writetiff_wiol: deflate compression\n"));
691 if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) {
692 mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor));
696 case COMPRESSION_PACKBITS:
697 mm_log((1, "i_writetiff_wiol: packbits compression\n"));
700 mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
704 linebytes = channels * width;
705 linebytes = TIFFScanlineSize(tif) > linebytes ? linebytes
706 : TIFFScanlineSize(tif);
707 /* working space for the scanlines - we go from 8-bit/pixel to 4 */
708 if (photometric == PHOTOMETRIC_PALETTE && bitspersample == 4)
709 linebytes += linebytes + 1;
710 linebuf = (unsigned char *)_TIFFmalloc(linebytes);
712 if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
713 mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
715 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
716 TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
718 mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
719 mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
720 mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
721 mm_log((1, "i_writetiff_wiol: bitspersample = %d\n", bitspersample));
723 got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
724 got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
725 if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
727 if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit))
728 resunit = RESUNIT_INCH;
729 if (got_xres || got_yres) {
735 resunit = RESUNIT_NONE;
738 if (resunit == RESUNIT_CENTIMETER) {
743 resunit = RESUNIT_INCH;
746 if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
747 i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
750 if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
751 i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
754 if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
755 i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
760 if (!save_tiff_tags(tif, im)) {
764 if (photometric == PHOTOMETRIC_PALETTE) {
765 for (y = 0; y < height; ++y) {
766 i_gpal(im, 0, width, y, linebuf);
767 if (bitspersample == 4)
768 pack_4bit_hl(linebuf, width);
769 if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
770 mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
771 if (linebuf) _TIFFfree(linebuf);
772 if (colors) _TIFFfree(colors);
778 for (y=0; y<height; y++) {
780 for(x=0; x<width; x++) {
781 (void) i_gpix(im, x, y,&val);
782 for(ch=0; ch<channels; ch++)
783 linebuf[ci++] = val.channel[ch];
785 if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
786 mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
787 if (linebuf) _TIFFfree(linebuf);
788 if (colors) _TIFFfree(colors);
793 if (linebuf) _TIFFfree(linebuf);
794 if (colors) _TIFFfree(colors);
799 =item i_writetiff_multi_wiol(ig, imgs, count, fine_mode)
801 Stores an image in the iolayer object.
803 ig - io_object that defines source to write to
804 imgs,count - the images to write
810 i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
814 io_glue_commit_types(ig);
816 mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
819 /* FIXME: Enable the mmap interface */
821 tif = TIFFClientOpen("No name",
824 (TIFFReadWriteProc) ig->readcb,
825 (TIFFReadWriteProc) ig->writecb,
826 (TIFFSeekProc) comp_seek,
827 (TIFFCloseProc) ig->closecb,
828 (TIFFSizeProc) ig->sizecb,
829 (TIFFMapFileProc) NULL,
830 (TIFFUnmapFileProc) NULL);
835 mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
839 for (i = 0; i < count; ++i) {
840 if (!i_writetiff_low(tif, imgs[i])) {
845 if (!TIFFWriteDirectory(tif)) {
846 i_push_error(0, "Cannot write TIFF directory");
852 (void) TIFFClose(tif);
857 =item i_writetiff_multi_wiol_faxable(ig, imgs, count, fine_mode)
859 Stores an image in the iolayer object.
861 ig - io_object that defines source to write to
862 imgs,count - the images to write
863 fine_mode - select fine or normal mode fax images
870 i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
874 io_glue_commit_types(ig);
876 mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
879 /* FIXME: Enable the mmap interface */
881 tif = TIFFClientOpen("No name",
884 (TIFFReadWriteProc) ig->readcb,
885 (TIFFReadWriteProc) ig->writecb,
886 (TIFFSeekProc) comp_seek,
887 (TIFFCloseProc) ig->closecb,
888 (TIFFSizeProc) ig->sizecb,
889 (TIFFMapFileProc) NULL,
890 (TIFFUnmapFileProc) NULL);
895 mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
899 for (i = 0; i < count; ++i) {
900 if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
905 if (!TIFFWriteDirectory(tif)) {
906 i_push_error(0, "Cannot write TIFF directory");
912 (void) TIFFClose(tif);
917 =item i_writetiff_wiol(im, ig)
919 Stores an image in the iolayer object.
921 im - image object to write out
922 ig - io_object that defines source to write to
927 i_writetiff_wiol(i_img *img, io_glue *ig) {
931 io_glue_commit_types(ig);
933 mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig));
935 /* FIXME: Enable the mmap interface */
937 tif = TIFFClientOpen("No name",
940 (TIFFReadWriteProc) ig->readcb,
941 (TIFFReadWriteProc) ig->writecb,
942 (TIFFSeekProc) comp_seek,
943 (TIFFCloseProc) ig->closecb,
944 (TIFFSizeProc) ig->sizecb,
945 (TIFFMapFileProc) NULL,
946 (TIFFUnmapFileProc) NULL);
951 mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
955 if (!i_writetiff_low(tif, img)) {
960 (void) TIFFClose(tif);
967 =item i_writetiff_wiol_faxable(i_img *, io_glue *)
969 Stores an image in the iolayer object in faxable tiff format.
971 im - image object to write out
972 ig - io_object that defines source to write to
974 Note, this may be rewritten to use to simply be a call to a
975 lower-level function that gives more options for writing tiff at some
982 i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
986 io_glue_commit_types(ig);
988 mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", im, ig));
990 /* FIXME: Enable the mmap interface */
992 tif = TIFFClientOpen("No name",
995 (TIFFReadWriteProc) ig->readcb,
996 (TIFFReadWriteProc) ig->writecb,
997 (TIFFSeekProc) comp_seek,
998 (TIFFCloseProc) ig->closecb,
999 (TIFFSizeProc) ig->sizecb,
1000 (TIFFMapFileProc) NULL,
1001 (TIFFUnmapFileProc) NULL);
1006 mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
1010 if (!i_writetiff_low_faxable(tif, im, fine)) {
1015 (void) TIFFClose(tif);
1019 static int save_tiff_tags(TIFF *tif, i_img *im) {
1022 for (i = 0; i < text_tag_count; ++i) {
1024 if (i_tags_find(&im->tags, text_tag_names[i].name, 0, &entry)) {
1025 if (!TIFFSetField(tif, text_tag_names[i].tag,
1026 im->tags.tags[entry].data)) {
1027 i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name);
1038 =item expand_4bit_hl(buf, count)
1040 Expands 4-bit/entry packed data into 1 byte/entry.
1042 buf must contain count bytes to be expanded and have 2*count bytes total
1045 The data is expanded in place.
1050 static void expand_4bit_hl(unsigned char *buf, int count) {
1051 while (--count >= 0) {
1052 buf[count*2+1] = buf[count] & 0xF;
1053 buf[count*2] = buf[count] >> 4;
1057 static void pack_4bit_hl(unsigned char *buf, int count) {
1060 buf[i/2] = (buf[i] << 4) + buf[i+1];
1070 Arnar M. Hrafnkelsson <addi@umich.edu>