#include "image.h"
#include "tiffio.h"
#include "iolayer.h"
-
-
+#include "imagei.h"
/*
=head1 NAME
Some of these functions are internal.
-=over 4
+=over
=cut
*/
+#define byteswap_macro(x) \
+ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
+ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
+
+struct tag_name {
+ char *name;
+ uint32 tag;
+};
+
+static struct tag_name text_tag_names[] =
+{
+ { "tiff_documentname", TIFFTAG_DOCUMENTNAME, },
+ { "tiff_imagedescription", TIFFTAG_IMAGEDESCRIPTION, },
+ { "tiff_make", TIFFTAG_MAKE, },
+ { "tiff_model", TIFFTAG_MODEL, },
+ { "tiff_pagename", TIFFTAG_PAGENAME, },
+ { "tiff_software", TIFFTAG_SOFTWARE, },
+ { "tiff_datetime", TIFFTAG_DATETIME, },
+ { "tiff_artist", TIFFTAG_ARTIST, },
+ { "tiff_hostcomputer", TIFFTAG_HOSTCOMPUTER, },
+};
+
+static const int text_tag_count =
+ sizeof(text_tag_names) / sizeof(*text_tag_names);
+
+static void error_handler(char const *module, char const *fmt, va_list ap) {
+ i_push_errorvf(0, fmt, ap);
+}
+
+static void warn_handler(char const *module, char const *fmt, va_list ap) {
+ /* for now do nothing, perhaps we could warn(), though that should be
+ done in the XS code, not in the code which isn't mean to know perl
+ exists ;) */
+}
+
+static int save_tiff_tags(TIFF *tif, i_img *im);
+
+static void expand_4bit_hl(unsigned char *buf, int count);
+
+static void pack_4bit_hl(unsigned char *buf, int count);
+static toff_t sizeproc(thandle_t x) {
+ return 0;
+}
+
/*
=item comp_seek(h, o, w)
return (toff_t) ig->seekcb(ig, o, w);
}
+/*
+=item comp_mmap(thandle_t, tdata_t*, toff_t*)
+
+Dummy mmap stub.
+
+This shouldn't ever be called but newer tifflibs want it anyway.
+
+=cut
+*/
+
+static
+int
+comp_mmap(thandle_t h, tdata_t*p, toff_t*off) {
+ return -1;
+}
/*
-=item i_readtiff_wiol(ig, length)
+=item comp_munmap(thandle_t h, tdata_t p, toff_t off)
-Retrieve an image and stores in the iolayer object. Returns NULL on fatal error.
+Dummy munmap stub.
- ig - io_glue object
- length - maximum length to read from data source, before closing it
+This shouldn't ever be called but newer tifflibs want it anyway.
=cut
*/
-i_img*
-i_readtiff_wiol(io_glue *ig, int length) {
+static void
+comp_munmap(thandle_t h, tdata_t p, toff_t off) {
+ /* do nothing */
+}
+
+static i_img *read_one_tiff(TIFF *tif) {
i_img *im;
uint32 width, height;
uint16 channels;
- uint32* raster;
+ uint32* raster = NULL;
int tiled, error;
- TIFF* tif;
+ float xres, yres;
+ uint16 resunit;
+ int gotXres, gotYres;
+ uint16 photometric;
+ uint16 bits_per_sample;
+ int i;
+ int ch;
error = 0;
- /* Add code to get the filename info from the iolayer */
- /* Also add code to check for mmapped code */
-
- io_glue_commit_types(ig);
- mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
-
- tif = TIFFClientOpen("Iolayer: FIXME",
- "rm",
- (thandle_t) ig,
- (TIFFReadWriteProc) ig->readcb,
- (TIFFReadWriteProc) ig->writecb,
- (TIFFSeekProc) comp_seek,
- (TIFFCloseProc) ig->closecb,
- (TIFFSizeProc) ig->sizecb,
- (TIFFMapFileProc) NULL,
- (TIFFUnmapFileProc) NULL);
-
- if (!tif) {
- mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
- return NULL;
- }
-
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
- TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels);
tiled = TIFFIsTiled(tif);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not "));
mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not "));
-
- im = i_img_empty_ch(NULL, width, height, channels);
+
+ if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) {
+ channels = 3;
+ im = i_img_pal_new(width, height, channels, 256);
+ }
+ else {
+ im = i_img_empty_ch(NULL, width, height, channels);
+ }
+
+ if (!im)
+ return NULL;
+
+ /* resolution tags */
+ TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit);
+ gotXres = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres);
+ gotYres = TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres);
+ if (gotXres || gotYres) {
+ if (!gotXres)
+ xres = yres;
+ else if (!gotYres)
+ yres = xres;
+ if (resunit == RESUNIT_CENTIMETER) {
+ /* from dots per cm to dpi */
+ xres *= 2.54;
+ yres *= 2.54;
+ }
+ i_tags_addn(&im->tags, "tiff_resolutionunit", 0, resunit);
+ if (resunit == RESUNIT_NONE)
+ i_tags_addn(&im->tags, "i_aspect_only", 0, 1);
+ i_tags_set_float(&im->tags, "i_xres", 0, xres);
+ i_tags_set_float(&im->tags, "i_yres", 0, yres);
+ }
+
+ /* Text tags */
+ for (i = 0; i < text_tag_count; ++i) {
+ char *data;
+ if (TIFFGetField(tif, text_tag_names[i].tag, &data)) {
+ mm_log((1, "i_readtiff_wiol: tag %d has value %s\n",
+ text_tag_names[i].tag, data));
+ i_tags_add(&im->tags, text_tag_names[i].name, 0, data,
+ strlen(data), 0);
+ }
+ }
+
+ i_tags_add(&im->tags, "i_format", 0, "tiff", -1, 0);
/* TIFFPrintDirectory(tif, stdout, 0); good for debugging */
-
- if (tiled) {
- int ok = 1;
- uint32 row, col;
- uint32 tile_width, tile_height;
- TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
- TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
- mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
+ if (photometric == PHOTOMETRIC_PALETTE &&
+ (bits_per_sample == 4 || bits_per_sample == 8)) {
+ uint16 *maps[3];
+ char used[256];
+ int maxused;
+ uint32 row, col;
+ unsigned char *buffer;
- raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
- if (!raster) {
- TIFFError(TIFFFileName(tif), "No space for raster buffer");
+ if (!TIFFGetField(tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) {
+ i_push_error(0, "Cannot get colormap for paletted image");
+ i_img_destroy(im);
return NULL;
}
-
- for( row = 0; row < height; row += tile_height ) {
- for( col = 0; ok && col < width; col += tile_width ) {
- uint32 i_row, x, newrows, newcols;
-
- /* Read the tile into an RGBA array */
- if (!TIFFReadRGBATile(tif, col, row, raster)) {
- ok = 0;
- break;
- }
- newrows = (row+tile_height > height) ? height-row : tile_height;
- mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
- newcols = (col+tile_width > width ) ? width-row : tile_width;
- for( i_row = 0; i_row < tile_height; i_row++ ) {
- for(x = 0; x < newcols; x++) {
- i_color val; /* FIXME: Make sure this works everywhere */
- val.ui = raster[x+tile_width*(tile_height-i_row-1)];
- i_ppix(im, col+x, row+i_row, &val);
- }
- }
+ buffer = (unsigned char *)_TIFFmalloc(width+2);
+ if (!buffer) {
+ i_push_error(0, "out of memory");
+ i_img_destroy(im);
+ return NULL;
+ }
+ row = 0;
+ memset(used, 0, sizeof(used));
+ while (row < height && TIFFReadScanline(tif, buffer, row, 0) > 0) {
+ if (bits_per_sample == 4)
+ expand_4bit_hl(buffer, (width+1)/2);
+ for (col = 0; col < width; ++col) {
+ used[buffer[col]] = 1;
}
+ i_ppal(im, 0, width, row, buffer);
+ ++row;
}
- } else {
- uint32 rowsperstrip, row;
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- mm_log((1, "i_readtiff_wiol: rowsperstrip=%d\n", rowsperstrip));
-
- raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
- if (!raster) {
- TIFFError(TIFFFileName(tif), "No space for raster buffer");
- return NULL;
+ if (row < height) {
+ error = 1;
}
-
- for( row = 0; row < height; row += rowsperstrip ) {
- uint32 newrows, i_row;
+ /* Ideally we'd optimize the palette, but that could be expensive
+ since we'd have to re-index every pixel.
+
+ Optimizing the palette (even at this level) might not
+ be what the user wants, so I don't do it.
+
+ We'll add a function to optimize a paletted image instead.
+ */
+ maxused = (1 << bits_per_sample)-1;
+ if (!error) {
+ while (maxused >= 0 && !used[maxused])
+ --maxused;
+ }
+ for (i = 0; i < 1 << bits_per_sample; ++i) {
+ i_color c;
+ for (ch = 0; ch < 3; ++ch) {
+ c.channel[ch] = Sample16To8(maps[ch][i]);
+ }
+ i_addcolors(im, &c, 1);
+ }
+ _TIFFfree(buffer);
+ }
+ else {
+ if (tiled) {
+ int ok = 1;
+ uint32 row, col;
+ uint32 tile_width, tile_height;
- if (!TIFFReadRGBAStrip(tif, row, raster)) {
- error++;
- break;
+ TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width);
+ TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height);
+ mm_log((1, "i_readtiff_wiol: tile_width=%d, tile_height=%d\n", tile_width, tile_height));
+
+ raster = (uint32*)_TIFFmalloc(tile_width * tile_height * sizeof (uint32));
+ if (!raster) {
+ i_img_destroy(im);
+ i_push_error(0, "No space for raster buffer");
+ return NULL;
}
- newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
- mm_log((1, "newrows=%d\n", newrows));
+ for( row = 0; row < height; row += tile_height ) {
+ for( col = 0; ok && col < width; col += tile_width ) {
+ uint32 i_row, x, newrows, newcols;
+
+ /* Read the tile into an RGBA array */
+ if (!TIFFReadRGBATile(tif, col, row, raster)) {
+ ok = 0;
+ break;
+ }
+ newrows = (row+tile_height > height) ? height-row : tile_height;
+ mm_log((1, "i_readtiff_wiol: newrows=%d\n", newrows));
+ newcols = (col+tile_width > width ) ? width-row : tile_width;
+ for( i_row = 0; i_row < tile_height; i_row++ ) {
+ for(x = 0; x < newcols; x++) {
+ i_color val;
+ uint32 temp = raster[x+tile_width*(tile_height-i_row-1)];
+ val.rgba.r = TIFFGetR(temp);
+ val.rgba.g = TIFFGetG(temp);
+ val.rgba.b = TIFFGetB(temp);
+ val.rgba.a = TIFFGetA(temp);
+ i_ppix(im, col+x, row+i_row, &val);
+ }
+ }
+ }
+ }
+ } else {
+ uint32 rowsperstrip, row;
+ int rc = TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ mm_log((1, "i_readtiff_wiol: rowsperstrip=%d rc = %d\n", rowsperstrip, rc));
+
+ if (rc != 1 || rowsperstrip==-1) {
+ rowsperstrip = height;
+ }
+
+ raster = (uint32*)_TIFFmalloc(width * rowsperstrip * sizeof (uint32));
+ if (!raster) {
+ i_img_destroy(im);
+ i_push_error(0, "No space for raster buffer");
+ return NULL;
+ }
- for( i_row = 0; i_row < newrows; i_row++ ) {
- uint32 x;
- for(x = 0; x<width; x++) {
- i_color val; /* FIXME: Make sure this works everywhere */
- val.ui = raster[x+width*(newrows-i_row-1)];
- i_ppix(im, x, i_row+row, &val);
- }
+ for( row = 0; row < height; row += rowsperstrip ) {
+ uint32 newrows, i_row;
+
+ if (!TIFFReadRGBAStrip(tif, row, raster)) {
+ error++;
+ break;
+ }
+
+ newrows = (row+rowsperstrip > height) ? height-row : rowsperstrip;
+ mm_log((1, "newrows=%d\n", newrows));
+
+ for( i_row = 0; i_row < newrows; i_row++ ) {
+ uint32 x;
+ for(x = 0; x<width; x++) {
+ i_color val;
+ uint32 temp = raster[x+width*(newrows-i_row-1)];
+ val.rgba.r = TIFFGetR(temp);
+ val.rgba.g = TIFFGetG(temp);
+ val.rgba.b = TIFFGetB(temp);
+ val.rgba.a = TIFFGetA(temp);
+ i_ppix(im, x, i_row+row, &val);
+ }
+ }
}
}
-
}
if (error) {
mm_log((1, "i_readtiff_wiol: error during reading\n"));
+ i_tags_addn(&im->tags, "i_incomplete", 0, 1);
}
- _TIFFfree( raster );
- if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
+ if (raster)
+ _TIFFfree( raster );
+
return im;
}
-
-
/*
-=item i_writetif_wiol(im, ig)
-
-Stores an image in the iolayer object.
-
- im - image object to write out
- ig - io_object that defines source to write to
+=item i_readtiff_wiol(im, ig)
-=cut
+=cut
*/
-
-
-/* FIXME: Add an options array in here soonish */
-
-undef_int
-i_writetiff_wiol(i_img *im, io_glue *ig) {
- uint32 width, height;
- uint16 channels;
- uint16 predictor = 0;
- int quality = 75;
- int jpegcolormode = JPEGCOLORMODE_RGB;
- uint16 compression = COMPRESSION_PACKBITS;
- i_color val;
- uint16 photometric;
- uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
- double resolution = -1;
- unsigned char *linebuf = NULL;
- uint32 y;
- tsize_t linebytes;
- int ch, ci, rc;
- uint32 x;
+i_img*
+i_readtiff_wiol(io_glue *ig, int length) {
TIFF* tif;
+ TIFFErrorHandler old_handler;
+ TIFFErrorHandler old_warn_handler;
+ i_img *im;
- char *cc = mymalloc( 123 );
- myfree(cc);
-
-
- width = im->xsize;
- height = im->ysize;
- channels = im->channels;
-
- switch (channels) {
- case 1:
- photometric = PHOTOMETRIC_MINISBLACK;
- break;
- case 3:
- photometric = PHOTOMETRIC_RGB;
- if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
- break;
- default:
- /* This means a colorspace we don't handle yet */
- mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
- return 0;
- }
+ i_clear_error();
+ old_handler = TIFFSetErrorHandler(error_handler);
+ old_warn_handler = TIFFSetWarningHandler(warn_handler);
/* Add code to get the filename info from the iolayer */
/* Also add code to check for mmapped code */
io_glue_commit_types(ig);
- mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));
-
- /* FIXME: Enable the mmap interface */
+ mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
- tif = TIFFClientOpen("No name",
- "wm",
- (thandle_t) ig,
+ tif = TIFFClientOpen("(Iolayer)",
+ "rm",
+ (thandle_t) ig,
(TIFFReadWriteProc) ig->readcb,
(TIFFReadWriteProc) ig->writecb,
- (TIFFSeekProc) comp_seek,
- (TIFFCloseProc) ig->closecb,
- (TIFFSizeProc) ig->sizecb,
- (TIFFMapFileProc) NULL,
- (TIFFUnmapFileProc) NULL);
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
-
-
if (!tif) {
- mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
- return 0;
+ mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
+ i_push_error(0, "opening file");
+ TIFFSetErrorHandler(old_handler);
+ TIFFSetWarningHandler(old_warn_handler);
+ return NULL;
}
- mm_log((1, "i_writetiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels));
-
- if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels)) { mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", channels)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=8\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) { mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) { mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); return 0; }
+ im = read_one_tiff(tif);
- switch (compression) {
- case COMPRESSION_JPEG:
- mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
- if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) { mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode)); return 0; }
- break;
- case COMPRESSION_LZW:
- mm_log((1, "i_writetiff_wiol: lzw compression\n"));
- break;
- case COMPRESSION_DEFLATE:
- mm_log((1, "i_writetiff_wiol: deflate compression\n"));
- if (predictor != 0)
- if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) { mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor)); return 0; }
- break;
- case COMPRESSION_PACKBITS:
- mm_log((1, "i_writetiff_wiol: packbits compression\n"));
- break;
- default:
- mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
- return 0;
- }
-
- linebytes = channels * width;
- linebuf = (unsigned char *)_TIFFmalloc( TIFFScanlineSize(tif) > linebytes ?
- linebytes : TIFFScanlineSize(tif) );
-
- if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
+ if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n"));
+ TIFFSetErrorHandler(old_handler);
+ TIFFSetWarningHandler(old_warn_handler);
+ TIFFClose(tif);
+ return im;
+}
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
- TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
+/*
+=item i_readtiff_multi_wiol(ig, length, *count)
- mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
- mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
- mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
+Reads multiple images from a TIFF.
- if (resolution > 0) {
- if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Xresolution=%d\n", resolution)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, resolution)) { mm_log((1, "i_writetiff_wiol: TIFFSetField Yresolution=%d\n", resolution)); return 0; }
- if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)) {
- mm_log((1, "i_writetiff_wiol: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
- }
- }
-
- for (y=0; y<height; y++) {
- ci = 0;
- for(x=0; x<width; x++) {
- (void) i_gpix(im, x, y,&val);
- for(ch=0; ch<channels; ch++) linebuf[ci++] = val.channel[ch];
- }
- if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
- mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
- break;
- }
- }
- (void) TIFFClose(tif);
- if (linebuf) _TIFFfree(linebuf);
- return 1;
-}
+=cut
+*/
+i_img**
+i_readtiff_multi_wiol(io_glue *ig, int length, int *count) {
+ TIFF* tif;
+ TIFFErrorHandler old_handler;
+ TIFFErrorHandler old_warn_handler;
+ i_img **results = NULL;
+ int result_alloc = 0;
+ int dirnum = 0;
-/*
-=item i_writetiff_wiol_faxable(i_img *, io_glue *)
+ i_clear_error();
+ old_handler = TIFFSetErrorHandler(error_handler);
+ old_warn_handler = TIFFSetWarningHandler(warn_handler);
-Stores an image in the iolayer object in faxable tiff format.
+ /* Add code to get the filename info from the iolayer */
+ /* Also add code to check for mmapped code */
- im - image object to write out
- ig - io_object that defines source to write to
+ io_glue_commit_types(ig);
+ mm_log((1, "i_readtiff_wiol(ig %p, length %d)\n", ig, length));
+
+ tif = TIFFClientOpen("(Iolayer)",
+ "rm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+ if (!tif) {
+ mm_log((1, "i_readtiff_wiol: Unable to open tif file\n"));
+ i_push_error(0, "opening file");
+ TIFFSetErrorHandler(old_handler);
+ TIFFSetWarningHandler(old_warn_handler);
+ return NULL;
+ }
-Note, this may be rewritten to use to simply be a call to a
-lower-level function that gives more options for writing tiff at some
-point.
+ *count = 0;
+ do {
+ i_img *im = read_one_tiff(tif);
+ if (!im)
+ break;
+ if (++*count > result_alloc) {
+ if (result_alloc == 0) {
+ result_alloc = 5;
+ results = mymalloc(result_alloc * sizeof(i_img *));
+ }
+ else {
+ i_img **newresults;
+ result_alloc *= 2;
+ newresults = myrealloc(results, result_alloc * sizeof(i_img *));
+ if (!newresults) {
+ i_img_destroy(im); /* don't leak it */
+ break;
+ }
+ results = newresults;
+ }
+ }
+ results[*count-1] = im;
+ } while (TIFFSetDirectory(tif, ++dirnum));
-=cut
-*/
+ TIFFSetWarningHandler(old_warn_handler);
+ TIFFSetErrorHandler(old_handler);
+ TIFFClose(tif);
+ return results;
+}
undef_int
-i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
+i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) {
uint32 width, height;
unsigned char *linebuf = NULL;
uint32 y;
int rc;
uint32 x;
- TIFF* tif;
- int luma_channel;
+ int luma_mask;
uint32 rowsperstrip;
float vres = fine ? 196 : 98;
+ int luma_chan;
width = im->xsize;
height = im->ysize;
switch (im->channels) {
case 1:
- luma_channel = 0;
+ case 2:
+ luma_chan = 0;
break;
case 3:
- luma_channel = 1;
+ case 4:
+ luma_chan = 1;
break;
default:
/* This means a colorspace we don't handle yet */
/* Add code to get the filename info from the iolayer */
/* Also add code to check for mmapped code */
- io_glue_commit_types(ig);
- mm_log((1, "i_writetiff_wiol_faxable(im 0x%p, ig 0x%p)\n", im, ig));
-
- /* FIXME: Enable the mmap interface */
-
- tif = TIFFClientOpen("No name",
- "wm",
- (thandle_t) ig,
- (TIFFReadWriteProc) ig->readcb,
- (TIFFReadWriteProc) ig->writecb,
- (TIFFSeekProc) comp_seek,
- (TIFFCloseProc) ig->closecb,
- (TIFFSizeProc) ig->sizecb,
- (TIFFMapFileProc) NULL,
- (TIFFUnmapFileProc) NULL);
-
- if (!tif) {
- mm_log((1, "i_writetiff_wiol_faxable: Unable to open tif file for writing\n"));
- return 0;
- }
mm_log((1, "i_writetiff_wiol_faxable: width=%d, height=%d, channels=%d\n", width, height, im->channels));
mm_log((1, "i_writetiff_wiol_faxable: TIFFSetField ResolutionUnit=%d\n", RESUNIT_INCH)); return 0;
}
+ if (!save_tiff_tags(tif, im)) {
+ return 0;
+ }
+
for (y=0; y<height; y++) {
int linebufpos=0;
for(x=0; x<width; x+=8) {
int bits;
int bitpos;
+ i_sample_t luma[8];
uint8 bitval = 128;
linebuf[linebufpos]=0;
bits = width-x; if(bits>8) bits=8;
+ i_gsamp(im, x, x+8, y, luma, &luma_chan, 1);
for(bitpos=0;bitpos<bits;bitpos++) {
- int luma;
- luma = im->data[(x+bitpos+y*im->xsize)*im->channels+luma_channel];
- linebuf[linebufpos] |= ((luma>=128)?bitval:0);
+ linebuf[linebufpos] |= ((luma[bitpos]>=128)?bitval:0);
bitval >>= 1;
}
linebufpos++;
break;
}
}
- (void) TIFFClose(tif);
if (linebuf) _TIFFfree(linebuf);
+
return 1;
}
+undef_int
+i_writetiff_low(TIFF *tif, i_img *im) {
+ uint32 width, height;
+ uint16 channels;
+ uint16 predictor = 0;
+ int quality = 75;
+ int jpegcolormode = JPEGCOLORMODE_RGB;
+ uint16 compression = COMPRESSION_PACKBITS;
+ i_color val;
+ uint16 photometric;
+ uint32 rowsperstrip = (uint32) -1; /* Let library pick default */
+ unsigned char *linebuf = NULL;
+ uint32 y;
+ tsize_t linebytes;
+ int ch, ci, rc;
+ uint32 x;
+ int got_xres, got_yres, got_aspectonly, aspect_only, resunit;
+ double xres, yres;
+ uint16 bitspersample = 8;
+ uint16 samplesperpixel;
+ uint16 *colors = NULL;
+
+ width = im->xsize;
+ height = im->ysize;
+ channels = im->channels;
+
+ switch (channels) {
+ case 1:
+ photometric = PHOTOMETRIC_MINISBLACK;
+ break;
+ case 3:
+ photometric = PHOTOMETRIC_RGB;
+ if (compression == COMPRESSION_JPEG && jpegcolormode == JPEGCOLORMODE_RGB) photometric = PHOTOMETRIC_YCBCR;
+ else if (im->type == i_palette_type) {
+ photometric = PHOTOMETRIC_PALETTE;
+ }
+ break;
+ default:
+ /* This means a colorspace we don't handle yet */
+ mm_log((1, "i_writetiff_wiol: don't handle %d channel images.\n", channels));
+ return 0;
+ }
+
+ /* Add code to get the filename info from the iolayer */
+ /* Also add code to check for mmapped code */
+
+ /*io_glue_commit_types(ig);*/
+ /*mm_log((1, "i_writetiff_wiol(im 0x%p, ig 0x%p)\n", im, ig));*/
+
+ mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d\n", width, height, channels));
+
+ if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField width=%d failed\n", width));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n"));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n"));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField photometric=%d\n", photometric));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression));
+ return 0;
+ }
+ samplesperpixel = channels;
+ if (photometric == PHOTOMETRIC_PALETTE) {
+ uint16 *out[3];
+ i_color c;
+ int count = i_colorcount(im);
+ int size;
+ int bits;
+ int ch, i;
+
+ samplesperpixel = 1;
+ if (count > 16)
+ bitspersample = 8;
+ else
+ bitspersample = 4;
+ size = 1 << bitspersample;
+ colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size);
+ out[0] = colors;
+ out[1] = colors + size;
+ out[2] = colors + 2 * size;
+
+ for (i = 0; i < count; ++i) {
+ i_getcolors(im, i, &c, 1);
+ for (ch = 0; ch < 3; ++ch)
+ out[ch][i] = c.channel[ch] * 257;
+ }
+ for (; i < size; ++i) {
+ for (ch = 0; ch < 3; ++ch)
+ out[ch][i] = 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
+ bitspersample));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_COLORMAP, out[0], out[1], out[2])) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField colormap\n"));
+ return 0;
+ }
+ }
+ else {
+ if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitspersample)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField bitpersample=%d\n",
+ bitspersample));
+ return 0;
+ }
+ }
+ if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel));
+ return 0;
+ }
+
+ switch (compression) {
+ case COMPRESSION_JPEG:
+ mm_log((1, "i_writetiff_wiol: jpeg compression\n"));
+ if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, quality) ) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField jpegquality=%d\n", quality));
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_JPEGCOLORMODE, jpegcolormode)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField jpegcolormode=%d\n", jpegcolormode));
+ return 0;
+ }
+ break;
+ case COMPRESSION_LZW:
+ mm_log((1, "i_writetiff_wiol: lzw compression\n"));
+ break;
+ case COMPRESSION_DEFLATE:
+ mm_log((1, "i_writetiff_wiol: deflate compression\n"));
+ if (predictor != 0)
+ if (!TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField predictor=%d\n", predictor));
+ return 0;
+ }
+ break;
+ case COMPRESSION_PACKBITS:
+ mm_log((1, "i_writetiff_wiol: packbits compression\n"));
+ break;
+ default:
+ mm_log((1, "i_writetiff_wiol: unknown compression %d\n", compression));
+ return 0;
+ }
+
+ linebytes = channels * width;
+ linebytes = TIFFScanlineSize(tif) > linebytes ? linebytes
+ : TIFFScanlineSize(tif);
+ /* working space for the scanlines - we go from 8-bit/pixel to 4 */
+ if (photometric == PHOTOMETRIC_PALETTE && bitspersample == 4)
+ linebytes += linebytes + 1;
+ linebuf = (unsigned char *)_TIFFmalloc(linebytes);
+
+ if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, rowsperstrip))) {
+ mm_log((1, "i_writetiff_wiol: TIFFSetField rowsperstrip=%d\n", rowsperstrip)); return 0; }
+
+ TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
+ TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rc);
+
+ mm_log((1, "i_writetiff_wiol: TIFFGetField rowsperstrip=%d\n", rowsperstrip));
+ mm_log((1, "i_writetiff_wiol: TIFFGetField scanlinesize=%d\n", TIFFScanlineSize(tif) ));
+ mm_log((1, "i_writetiff_wiol: TIFFGetField planarconfig=%d == %d\n", rc, PLANARCONFIG_CONTIG));
+ mm_log((1, "i_writetiff_wiol: bitspersample = %d\n", bitspersample));
+
+ got_xres = i_tags_get_float(&im->tags, "i_xres", 0, &xres);
+ got_yres = i_tags_get_float(&im->tags, "i_yres", 0, &yres);
+ if (!i_tags_get_int(&im->tags, "i_aspect_only", 0,&aspect_only))
+ aspect_only = 0;
+ if (!i_tags_get_int(&im->tags, "tiff_resolutionunit", 0, &resunit))
+ resunit = RESUNIT_INCH;
+ if (got_xres || got_yres) {
+ if (!got_xres)
+ xres = yres;
+ else if (!got_yres)
+ yres = xres;
+ if (aspect_only) {
+ resunit = RESUNIT_NONE;
+ }
+ else {
+ if (resunit == RESUNIT_CENTIMETER) {
+ xres /= 2.54;
+ yres /= 2.54;
+ }
+ else {
+ resunit = RESUNIT_INCH;
+ }
+ }
+ if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) {
+ i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag");
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) {
+ i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag");
+ return 0;
+ }
+ if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) {
+ i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag");
+ return 0;
+ }
+ }
+
+ if (!save_tiff_tags(tif, im)) {
+ return 0;
+ }
+
+ if (photometric == PHOTOMETRIC_PALETTE) {
+ for (y = 0; y < height; ++y) {
+ i_gpal(im, 0, width, y, linebuf);
+ if (bitspersample == 4)
+ pack_4bit_hl(linebuf, width);
+ if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
+ mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
+ if (linebuf) _TIFFfree(linebuf);
+ if (colors) _TIFFfree(colors);
+ return 0;
+ }
+ }
+ }
+ else {
+ for (y=0; y<height; y++) {
+ ci = 0;
+ for(x=0; x<width; x++) {
+ (void) i_gpix(im, x, y,&val);
+ for(ch=0; ch<channels; ch++)
+ linebuf[ci++] = val.channel[ch];
+ }
+ if (TIFFWriteScanline(tif, linebuf, y, 0) < 0) {
+ mm_log((1, "i_writetiff_wiol: TIFFWriteScanline failed.\n"));
+ if (linebuf) _TIFFfree(linebuf);
+ if (colors) _TIFFfree(colors);
+ return 0;
+ }
+ }
+ }
+ if (linebuf) _TIFFfree(linebuf);
+ if (colors) _TIFFfree(colors);
+ return 1;
+}
+
+/*
+=item i_writetiff_multi_wiol(ig, imgs, count, fine_mode)
+
+Stores an image in the iolayer object.
+
+ ig - io_object that defines source to write to
+ imgs,count - the images to write
+
+=cut
+*/
+
+undef_int
+i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
+ ig, imgs, count));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
+ return 0;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (!i_writetiff_low(tif, imgs[i])) {
+ TIFFClose(tif);
+ return 0;
+ }
+
+ if (!TIFFWriteDirectory(tif)) {
+ i_push_error(0, "Cannot write TIFF directory");
+ TIFFClose(tif);
+ return 0;
+ }
+ }
+
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+/*
+=item i_writetiff_multi_wiol_faxable(ig, imgs, count, fine_mode)
+
+Stores an image in the iolayer object.
+
+ ig - io_object that defines source to write to
+ imgs,count - the images to write
+ fine_mode - select fine or normal mode fax images
+
+=cut
+*/
+
+
+undef_int
+i_writetiff_multi_wiol_faxable(io_glue *ig, i_img **imgs, int count, int fine) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_multi_wiol(ig 0x%p, imgs 0x%p, count %d)\n",
+ ig, imgs, count));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_mulit_wiol: Unable to open tif file for writing\n"));
+ return 0;
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (!i_writetiff_low_faxable(tif, imgs[i], fine)) {
+ TIFFClose(tif);
+ return 0;
+ }
+
+ if (!TIFFWriteDirectory(tif)) {
+ i_push_error(0, "Cannot write TIFF directory");
+ TIFFClose(tif);
+ return 0;
+ }
+ }
+
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+/*
+=item i_writetiff_wiol(im, ig)
+
+Stores an image in the iolayer object.
+
+ im - image object to write out
+ ig - io_object that defines source to write to
+
+=cut
+*/
+undef_int
+i_writetiff_wiol(i_img *img, io_glue *ig) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", img, ig));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+ return 0;
+ }
+
+ if (!i_writetiff_low(tif, img)) {
+ TIFFClose(tif);
+ return 0;
+ }
+
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+
+
+/*
+=item i_writetiff_wiol_faxable(i_img *, io_glue *)
+
+Stores an image in the iolayer object in faxable tiff format.
+
+ im - image object to write out
+ ig - io_object that defines source to write to
+
+Note, this may be rewritten to use to simply be a call to a
+lower-level function that gives more options for writing tiff at some
+point.
+
+=cut
+*/
+
+undef_int
+i_writetiff_wiol_faxable(i_img *im, io_glue *ig, int fine) {
+ TIFF* tif;
+ int i;
+
+ io_glue_commit_types(ig);
+ i_clear_error();
+ mm_log((1, "i_writetiff_wiol(img %p, ig 0x%p)\n", im, ig));
+
+ /* FIXME: Enable the mmap interface */
+
+ tif = TIFFClientOpen("No name",
+ "wm",
+ (thandle_t) ig,
+ (TIFFReadWriteProc) ig->readcb,
+ (TIFFReadWriteProc) ig->writecb,
+ (TIFFSeekProc) comp_seek,
+ (TIFFCloseProc) ig->closecb,
+ ig->sizecb ? (TIFFSizeProc) ig->sizecb : (TIFFSizeProc) sizeproc,
+ (TIFFMapFileProc) comp_mmap,
+ (TIFFUnmapFileProc) comp_munmap);
+
+
+
+ if (!tif) {
+ mm_log((1, "i_writetiff_wiol: Unable to open tif file for writing\n"));
+ return 0;
+ }
+
+ if (!i_writetiff_low_faxable(tif, im, fine)) {
+ TIFFClose(tif);
+ return 0;
+ }
+
+ (void) TIFFClose(tif);
+ return 1;
+}
+
+static int save_tiff_tags(TIFF *tif, i_img *im) {
+ int i;
+
+ for (i = 0; i < text_tag_count; ++i) {
+ int entry;
+ if (i_tags_find(&im->tags, text_tag_names[i].name, 0, &entry)) {
+ if (!TIFFSetField(tif, text_tag_names[i].tag,
+ im->tags.tags[entry].data)) {
+ i_push_errorf(0, "cannot save %s to TIFF", text_tag_names[i].name);
+ return 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+/*
+=item expand_4bit_hl(buf, count)
+
+Expands 4-bit/entry packed data into 1 byte/entry.
+
+buf must contain count bytes to be expanded and have 2*count bytes total
+space.
+
+The data is expanded in place.
+
+=cut
+*/
+
+static void expand_4bit_hl(unsigned char *buf, int count) {
+ while (--count >= 0) {
+ buf[count*2+1] = buf[count] & 0xF;
+ buf[count*2] = buf[count] >> 4;
+ }
+}
+
+static void pack_4bit_hl(unsigned char *buf, int count) {
+ int i = 0;
+ while (i < count) {
+ buf[i/2] = (buf[i] << 4) + buf[i+1];
+ i += 2;
+ }
+}
+
+/*
+=back
+
+=head1 AUTHOR
+
+Arnar M. Hrafnkelsson <addi@umich.edu>
+
+=head1 SEE ALSO
+
+Imager(3)
+
+=cut
+*/