+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
+*/