X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/9c106321e22c5a74d0e5d946b452db24e1c9d6f7..ed3c09a19185bbdaa970a9686cfd9c9a4f577c6a:/tiff.c diff --git a/tiff.c b/tiff.c index ccbcd554..23a0e822 100644 --- a/tiff.c +++ b/tiff.c @@ -1,8 +1,13 @@ #include "imager.h" -#include "tiffio.h" +#include #include "iolayer.h" #include "imageri.h" +/* needed to implement our substitute TIFFIsCODECConfigured */ +#if TIFFLIB_VERSION < 20031121 +static int TIFFIsCODECConfigured(uint16 scheme); +#endif + /* =head1 NAME @@ -35,11 +40,17 @@ Some of these functions are internal. ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) +#define CLAMP8(x) ((x) < 0 ? 0 : (x) > 255 ? 255 : (x)) +#define CLAMP16(x) ((x) < 0 ? 0 : (x) > 65535 ? 65535 : (x)) + struct tag_name { char *name; uint32 tag; }; +static i_img *read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete); +static i_img *read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete); + static struct tag_name text_tag_names[] = { { "tiff_documentname", TIFFTAG_DOCUMENTNAME, }, @@ -53,10 +64,105 @@ static struct tag_name text_tag_names[] = { "tiff_hostcomputer", TIFFTAG_HOSTCOMPUTER, }, }; +static struct tag_name +compress_values[] = + { + { "none", COMPRESSION_NONE }, + { "ccittrle", COMPRESSION_CCITTRLE }, + { "fax3", COMPRESSION_CCITTFAX3 }, + { "t4", COMPRESSION_CCITTFAX3 }, + { "fax4", COMPRESSION_CCITTFAX4 }, + { "t6", COMPRESSION_CCITTFAX4 }, + { "lzw", COMPRESSION_LZW }, + { "jpeg", COMPRESSION_JPEG }, + { "packbits", COMPRESSION_PACKBITS }, + { "deflate", COMPRESSION_ADOBE_DEFLATE }, + { "zip", COMPRESSION_ADOBE_DEFLATE }, + { "oldzip", COMPRESSION_DEFLATE }, + { "ccittrlew", COMPRESSION_CCITTRLEW }, + }; + +static const int compress_value_count = + sizeof(compress_values) / sizeof(*compress_values); + +static int +myTIFFIsCODECConfigured(uint16 scheme); + +typedef struct read_state_tag read_state_t; +/* the setup function creates the image object, allocates the line buffer */ +typedef int (*read_setup_t)(read_state_t *state); + +/* the putter writes the image data provided by the getter to the + image, x, y, width, height describe the target area of the image, + extras is the extra number of pixels stored for each scanline in + the raster buffer, (for tiles against the right side of the + image) */ + +typedef int (*read_putter_t)(read_state_t *state, int x, int y, int width, + int height, int extras); + +/* reads from a tiled or strip image and calls the putter. + This may need a second type for handling non-contiguous images + at some point */ +typedef int (*read_getter_t)(read_state_t *state, read_putter_t putter); + +struct read_state_tag { + TIFF *tif; + i_img *img; + void *raster; + unsigned long pixels_read; + int allow_incomplete; + void *line_buf; + uint32 width, height; + uint16 bits_per_sample; + uint16 photometric; + + /* the total number of channels (samples per pixel) */ + int samples_per_pixel; + + /* if non-zero, which channel is the alpha channel, typically 3 for rgb */ + int alpha_chan; + + /* whether or not to scale the color channels based on the alpha + channel. TIFF has 2 types of alpha channel, if the alpha channel + we use is EXTRASAMPLE_ASSOCALPHA then the color data will need to + be scaled to match Imager's conventions */ + int scale_alpha; +}; + +static int tile_contig_getter(read_state_t *state, read_putter_t putter); +static int strip_contig_getter(read_state_t *state, read_putter_t putter); + +static int setup_paletted(read_state_t *state); +static int paletted_putter8(read_state_t *, int, int, int, int, int); +static int paletted_putter4(read_state_t *, int, int, int, int, int); + +static int setup_16_rgb(read_state_t *state); +static int setup_16_grey(read_state_t *state); +static int putter_16(read_state_t *, int, int, int, int, int); + +static int setup_8_rgb(read_state_t *state); +static int setup_8_grey(read_state_t *state); +static int putter_8(read_state_t *, int, int, int, int, int); + +static int setup_32_rgb(read_state_t *state); +static int setup_32_grey(read_state_t *state); +static int putter_32(read_state_t *, int, int, int, int, int); + +static int setup_bilevel(read_state_t *state); +static int putter_bilevel(read_state_t *, int, int, int, int, int); + +static int setup_cmyk8(read_state_t *state); +static int putter_cmyk8(read_state_t *, int, int, int, int, int); + +static int setup_cmyk16(read_state_t *state); +static int putter_cmyk16(read_state_t *, int, int, int, int, int); + 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) { + mm_log((1, "tiff error fmt %s\n", fmt)); i_push_errorvf(0, fmt, ap); } @@ -73,6 +179,8 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) { #else vsprintf(buf, fmt, ap); #endif + mm_log((1, "tiff warning %s\n", buf)); + if (!warn_buffer || strlen(warn_buffer)+strlen(buf)+2 > warn_buffer_size) { int new_size = warn_buffer_size + strlen(buf) + 2; char *old_buffer = warn_buffer; @@ -91,9 +199,8 @@ static void warn_handler(char const *module, char const *fmt, va_list ap) { 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 void +pack_4bit_to(unsigned char *dest, const unsigned char *src, int count); static toff_t sizeproc(thandle_t x) { @@ -151,59 +258,154 @@ comp_munmap(thandle_t h, tdata_t p, toff_t off) { /* do nothing */ } -static i_img *read_one_tiff(TIFF *tif, int allow_partial) { +static i_img *read_one_tiff(TIFF *tif, int allow_incomplete) { i_img *im; uint32 width, height; - uint16 channels; - uint32* raster = NULL; + uint16 samples_per_pixel; int tiled, error; float xres, yres; uint16 resunit; int gotXres, gotYres; uint16 photometric; uint16 bits_per_sample; + uint16 planar_config; + uint16 inkset; + uint16 compress; int i; - int ch; + read_state_t state; + read_setup_t setupf = NULL; + read_getter_t getterf = NULL; + read_putter_t putterf = NULL; error = 0; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); - TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &channels); + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); tiled = TIFFIsTiled(tif); TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric); TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); + TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config); + TIFFGetFieldDefaulted(tif, TIFFTAG_INKSET, &inkset); - mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, channels)); + mm_log((1, "i_readtiff_wiol: width=%d, height=%d, channels=%d\n", width, height, samples_per_pixel)); mm_log((1, "i_readtiff_wiol: %stiled\n", tiled?"":"not ")); mm_log((1, "i_readtiff_wiol: %sbyte swapped\n", TIFFIsByteSwapped(tif)?"":"not ")); - /* separated defaults to CMYK, but if the user is using some strange - ink system we can't work out the color anyway */ - if (photometric == PHOTOMETRIC_SEPARATED && channels >= 4) { - /* TIFF can have more than one alpha channel on an image, - but Imager can't, only store the first one */ - - channels = channels == 4 ? 3 : 4; - - /* unfortunately the RGBA functions don't try to deal with the alpha - channel on CMYK images, at some point I'm planning on expanding - TIFF support to handle 16-bit/sample images and I'll deal with - it then */ + /* yes, this if() is horrible */ + if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) { + setupf = setup_paletted; + if (bits_per_sample == 8) + putterf = paletted_putter8; + else if (bits_per_sample == 4) + putterf = paletted_putter4; + else + mm_log((1, "unsupported paletted bits_per_sample %d\n", bits_per_sample)); } + else if (bits_per_sample == 16 + && photometric == PHOTOMETRIC_RGB + && samples_per_pixel >= 3) { + setupf = setup_16_rgb; + putterf = putter_16; + } + else if (bits_per_sample == 16 + && photometric == PHOTOMETRIC_MINISBLACK) { + setupf = setup_16_grey; + putterf = putter_16; + } + else if (bits_per_sample == 8 + && photometric == PHOTOMETRIC_MINISBLACK) { + setupf = setup_8_grey; + putterf = putter_8; + } + else if (bits_per_sample == 8 + && photometric == PHOTOMETRIC_RGB) { + setupf = setup_8_rgb; + putterf = putter_8; + } + else if (bits_per_sample == 32 + && photometric == PHOTOMETRIC_RGB + && samples_per_pixel >= 3) { + setupf = setup_32_rgb; + putterf = putter_32; + } + else if (bits_per_sample == 32 + && photometric == PHOTOMETRIC_MINISBLACK) { + setupf = setup_32_grey; + putterf = putter_32; + } + else if (bits_per_sample == 1 + && (photometric == PHOTOMETRIC_MINISBLACK + || photometric == PHOTOMETRIC_MINISWHITE) + && samples_per_pixel == 1) { + setupf = setup_bilevel; + putterf = putter_bilevel; + } + else if (bits_per_sample == 8 + && photometric == PHOTOMETRIC_SEPARATED + && inkset == INKSET_CMYK + && samples_per_pixel >= 4) { + setupf = setup_cmyk8; + putterf = putter_cmyk8; + } + else if (bits_per_sample == 16 + && photometric == PHOTOMETRIC_SEPARATED + && inkset == INKSET_CMYK + && samples_per_pixel >= 4) { + setupf = setup_cmyk16; + putterf = putter_cmyk16; + } + if (tiled) { + if (planar_config == PLANARCONFIG_CONTIG) + getterf = tile_contig_getter; + } + else { + if (planar_config == PLANARCONFIG_CONTIG) + getterf = strip_contig_getter; + } + if (setupf && getterf && putterf) { + unsigned long total_pixels = (unsigned long)width * height; + memset(&state, 0, sizeof(state)); + state.tif = tif; + state.allow_incomplete = allow_incomplete; + state.width = width; + state.height = height; + state.bits_per_sample = bits_per_sample; + state.samples_per_pixel = samples_per_pixel; + state.photometric = photometric; + + if (!setupf(&state)) + return NULL; + if (!getterf(&state, putterf) || !state.pixels_read) { + if (state.img) + i_img_destroy(state.img); + if (state.raster) + _TIFFfree(state.raster); + if (state.line_buf) + myfree(state.line_buf); + + return NULL; + } - /* TIFF images can have more than one alpha channel, but Imager can't - this ignores the possibility of 2 channel images with 2 alpha, - but there's not much I can do about that */ - if (channels > 4) - channels = 4; - - if (photometric == PHOTOMETRIC_PALETTE && bits_per_sample <= 8) { - channels = 3; - im = i_img_pal_new(width, height, channels, 256); + if (allow_incomplete && state.pixels_read < total_pixels) { + i_tags_setn(&(state.img->tags), "i_incomplete", 1); + i_tags_setn(&(state.img->tags), "i_lines_read", + state.pixels_read / width); + } + im = state.img; + + if (state.raster) + _TIFFfree(state.raster); + if (state.line_buf) + myfree(state.line_buf); } else { - im = i_img_empty_ch(NULL, width, height, channels); + if (tiled) { + im = read_one_rgb_tiled(tif, width, height, allow_incomplete); + } + else { + im = read_one_rgb_lines(tif, width, height, allow_incomplete); + } } if (!im) @@ -212,6 +414,7 @@ static i_img *read_one_tiff(TIFF *tif, int allow_partial) { /* general metadata */ i_tags_addn(&im->tags, "tiff_bitspersample", 0, bits_per_sample); i_tags_addn(&im->tags, "tiff_photometric", 0, photometric); + TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &compress); /* resolution tags */ TIFFGetFieldDefaulted(tif, TIFFTAG_RESOLUTIONUNIT, &resunit); @@ -262,172 +465,14 @@ static i_img *read_one_tiff(TIFF *tif, int allow_partial) { i_tags_add(&im->tags, "i_warning", 0, warn_buffer, -1, 0); *warn_buffer = '\0'; } - - /* TIFFPrintDirectory(tif, stdout, 0); good for debugging */ - - 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; - - 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; - } - 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; - } - if (row < height) { - if (allow_partial) { - i_tags_setn(&im->tags, "i_lines_read", row); - } - else { - i_img_destroy(im); - _TIFFfree(buffer); - return NULL; - } - error = 1; - } - /* 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); + + for (i = 0; i < compress_value_count; ++i) { + if (compress_values[i].tag == compress) { + i_tags_add(&im->tags, "tiff_compression", 0, compress_values[i].name, -1, 0); + break; } - _TIFFfree(buffer); } - else { - 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)); - - 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; - } - - 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( row = 0; row < height; row += rowsperstrip ) { - uint32 newrows, i_row; - - if (!TIFFReadRGBAStrip(tif, row, raster)) { - if (allow_partial) { - i_tags_setn(&im->tags, "i_lines_read", row); - error++; - break; - } - else { - i_push_error(0, "could not read TIFF image strip"); - _TIFFfree(raster); - i_img_destroy(im); - return NULL; - } - } - - 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; xtags, "i_incomplete", 1); - } - if (raster) - _TIFFfree( raster ); - return im; } @@ -437,7 +482,7 @@ static i_img *read_one_tiff(TIFF *tif, int allow_partial) { =cut */ i_img* -i_readtiff_wiol(io_glue *ig, int allow_partial, int page) { +i_readtiff_wiol(io_glue *ig, int allow_incomplete, int page) { TIFF* tif; TIFFErrorHandler old_handler; TIFFErrorHandler old_warn_handler; @@ -453,7 +498,7 @@ i_readtiff_wiol(io_glue *ig, int allow_partial, int page) { /* Also add code to check for mmapped code */ io_glue_commit_types(ig); - mm_log((1, "i_readtiff_wiol(ig %p, allow_partial %d, page %d)\n", ig, allow_partial, page)); + mm_log((1, "i_readtiff_wiol(ig %p, allow_incomplete %d, page %d)\n", ig, allow_incomplete, page)); tif = TIFFClientOpen("(Iolayer)", "rm", @@ -485,7 +530,7 @@ i_readtiff_wiol(io_glue *ig, int allow_partial, int page) { } } - im = read_one_tiff(tif, allow_partial); + im = read_one_tiff(tif, allow_incomplete); if (TIFFLastDirectory(tif)) mm_log((1, "Last directory of tiff file\n")); TIFFSetErrorHandler(old_handler); @@ -673,180 +718,92 @@ i_writetiff_low_faxable(TIFF *tif, i_img *im, int fine) { 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, aspect_only, resunit; - double xres, yres; - uint16 bitspersample = 8; - uint16 samplesperpixel; - uint16 *colors = NULL; - - width = im->xsize; - height = im->ysize; - channels = im->channels; +static uint16 +find_compression(char const *name, uint16 *compress) { + int i; - 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; + for (i = 0; i < compress_value_count; ++i) { + if (strcmp(compress_values[i].name, name) == 0) { + *compress = (uint16)compress_values[i].tag; + return 1; } - 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; } + *compress = COMPRESSION_NONE; - /* 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));*/ + return 0; +} - 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; +static uint16 +get_compression(i_img *im, uint16 def_compress) { + int entry; + int value; + + if (i_tags_find(&im->tags, "tiff_compression", 0, &entry) + && im->tags.tags[entry].data) { + uint16 compress; + if (find_compression(im->tags.tags[entry].data, &compress) + && myTIFFIsCODECConfigured(compress)) + return compress; } - if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height) ) { - mm_log((1, "i_writetiff_wiol: TIFFSetField length=%d failed\n", height)); - return 0; + if (i_tags_get_int(&im->tags, "tiff_compression", 0, &value)) { + if ((uint16)value == value + && myTIFFIsCODECConfigured((uint16)value)) + return (uint16)value; } - if (!TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT)) { - mm_log((1, "i_writetiff_wiol: TIFFSetField Orientation=topleft\n")); - return 0; + + return def_compress; +} + +int +i_tiff_has_compression(const char *name) { + uint16 compress; + + if (!find_compression(name, &compress)) + return 0; + + return myTIFFIsCODECConfigured(compress); +} + +static int +set_base_tags(TIFF *tif, i_img *im, uint16 compress, uint16 photometric, + uint16 bits_per_sample, uint16 samples_per_pixel) { + double xres, yres; + int resunit; + int got_xres, got_yres; + int aspect_only; + + if (!TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, im->xsize)) { + i_push_error(0, "write TIFF: setting width tag"); + return 0; } - if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { - mm_log((1, "i_writetiff_wiol: TIFFSetField planarconfig\n")); - return 0; + if (!TIFFSetField(tif, TIFFTAG_IMAGELENGTH, im->ysize)) { + i_push_error(0, "write TIFF: setting length tag"); + 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_ORIENTATION, ORIENTATION_TOPLEFT)) { + i_push_error(0, "write TIFF: setting orientation tag"); + return 0; } - if (!TIFFSetField(tif, TIFFTAG_COMPRESSION, compression)) { - mm_log((1, "i_writetiff_wiol: TIFFSetField compression=%d\n", compression)); - return 0; + if (!TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) { + i_push_error(0, "write TIFF: setting planar configuration tag"); + return 0; } - samplesperpixel = channels; - if (photometric == PHOTOMETRIC_PALETTE) { - uint16 *out[3]; - i_color c; - int count = i_colorcount(im); - int size; - 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; - } + if (!TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, photometric)) { + i_push_error(0, "write TIFF: setting photometric tag"); + 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_COMPRESSION, compress)) { + i_push_error(0, "write TIFF: setting compression tag"); + return 0; } - if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel)) { - mm_log((1, "i_writetiff_wiol: TIFFSetField samplesperpixel=%d failed\n", samplesperpixel)); + if (!TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bits_per_sample)) { + i_push_error(0, "write TIFF: setting bits per sample tag"); 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)); + if (!TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, samples_per_pixel)) { + i_push_error(0, "write TIFF: setting samples per pixel tag"); 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); @@ -872,114 +829,483 @@ i_writetiff_low(TIFF *tif, i_img *im) { } } if (!TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)xres)) { - i_push_error(0, "cannot set TIFFTAG_XRESOLUTION tag"); + i_push_error(0, "write TIFF: setting xresolution tag"); return 0; } if (!TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)yres)) { - i_push_error(0, "cannot set TIFFTAG_YRESOLUTION tag"); + i_push_error(0, "write TIFF: setting yresolution tag"); return 0; } if (!TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, (uint16)resunit)) { - i_push_error(0, "cannot set TIFFTAG_RESOLUTIONUNIT tag"); + i_push_error(0, "write TIFF: setting resolutionunit tag"); return 0; } } - if (!save_tiff_tags(tif, im)) { - return 0; + return 1; +} + +static int +write_one_bilevel(TIFF *tif, i_img *im, int zero_is_white) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + uint16 photometric; + unsigned char *in_row; + unsigned char *out_row; + unsigned out_size; + int x, y; + int invert; + + mm_log((1, "tiff - write_one_bilevel(tif %p, im %p, zero_is_white %d)\n", + tif, im, zero_is_white)); + + /* ignore a silly choice */ + if (compress == COMPRESSION_JPEG) + compress = COMPRESSION_PACKBITS; + + switch (compress) { + case COMPRESSION_CCITTRLE: + case COMPRESSION_CCITTFAX3: + case COMPRESSION_CCITTFAX4: + /* natural fax photometric */ + photometric = PHOTOMETRIC_MINISWHITE; + break; + + default: + /* natural for most computer images */ + photometric = PHOTOMETRIC_MINISBLACK; + break; } - 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; - } - } + if (!set_base_tags(tif, im, compress, photometric, 1, 1)) + return 0; + + if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) { + i_push_error(0, "write TIFF: setting rows per strip tag"); + return 0; } - else { - for (y=0; yxsize); + + invert = (photometric == PHOTOMETRIC_MINISWHITE) != (zero_is_white != 0); + + for (y = 0; y < im->ysize; ++y) { + int mask = 0x80; + unsigned char *outp = out_row; + memset(out_row, 0, out_size); + i_gpal(im, 0, im->xsize, y, in_row); + for (x = 0; x < im->xsize; ++x) { + if (invert ? !in_row[x] : in_row[x]) { + *outp |= mask; } - 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; + mask >>= 1; + if (!mask) { + ++outp; + mask = 0x80; } } + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + _TIFFfree(out_row); + myfree(in_row); + i_push_error(0, "write TIFF: write scan line failed"); + 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. + _TIFFfree(out_row); + myfree(in_row); - ig - io_object that defines source to write to - imgs,count - the images to write + return 1; +} -=cut -*/ +static int +set_palette(TIFF *tif, i_img *im, int size) { + int count; + uint16 *colors; + uint16 *out[3]; + i_color c; + int i, ch; + + colors = (uint16 *)_TIFFmalloc(sizeof(uint16) * 3 * size); + out[0] = colors; + out[1] = colors + size; + out[2] = colors + 2 * size; + + count = i_colorcount(im); + 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_COLORMAP, out[0], out[1], out[2])) { + _TIFFfree(colors); + i_push_error(0, "write TIFF: setting color map"); + return 0; + } + _TIFFfree(colors); + + return 1; +} -undef_int -i_writetiff_multi_wiol(io_glue *ig, i_img **imgs, int count) { - TIFF* tif; - TIFFErrorHandler old_handler; - int i; +static int +write_one_paletted8(TIFF *tif, i_img *im) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + unsigned char *out_row; + unsigned out_size; + int y; - old_handler = TIFFSetErrorHandler(error_handler); + mm_log((1, "tiff - write_one_paletted8(tif %p, im %p)\n", tif, im)); - 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)); + /* ignore a silly choice */ + if (compress == COMPRESSION_JPEG || + compress == COMPRESSION_CCITTRLE || + compress == COMPRESSION_CCITTFAX3 || + compress == COMPRESSION_CCITTFAX4) + compress = COMPRESSION_PACKBITS; - /* 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 (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) { + i_push_error(0, "write TIFF: setting rows per strip tag"); + return 0; + } + if (!set_base_tags(tif, im, compress, PHOTOMETRIC_PALETTE, 8, 1)) + return 0; - if (!tif) { - mm_log((1, "i_writetiff_multi_wiol: Unable to open tif file for writing\n")); - i_push_error(0, "Could not create TIFF object"); - TIFFSetErrorHandler(old_handler); + if (!set_palette(tif, im, 256)) return 0; - } - for (i = 0; i < count; ++i) { - if (!i_writetiff_low(tif, imgs[i])) { - TIFFClose(tif); - TIFFSetErrorHandler(old_handler); - return 0; - } + out_size = TIFFScanlineSize(tif); + out_row = (unsigned char *)_TIFFmalloc( out_size ); - if (!TIFFWriteDirectory(tif)) { - i_push_error(0, "Cannot write TIFF directory"); - TIFFClose(tif); - TIFFSetErrorHandler(old_handler); + for (y = 0; y < im->ysize; ++y) { + i_gpal(im, 0, im->xsize, y, out_row); + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + _TIFFfree(out_row); + i_push_error(0, "write TIFF: write scan line failed"); + return 0; + } + } + + _TIFFfree(out_row); + + return 1; +} + +static int +write_one_paletted4(TIFF *tif, i_img *im) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + unsigned char *in_row; + unsigned char *out_row; + unsigned out_size; + int y; + + mm_log((1, "tiff - write_one_paletted4(tif %p, im %p)\n", tif, im)); + + /* ignore a silly choice */ + if (compress == COMPRESSION_JPEG || + compress == COMPRESSION_CCITTRLE || + compress == COMPRESSION_CCITTFAX3 || + compress == COMPRESSION_CCITTFAX4) + compress = COMPRESSION_PACKBITS; + + if (!set_base_tags(tif, im, compress, PHOTOMETRIC_PALETTE, 4, 1)) + return 0; + + if (!set_palette(tif, im, 16)) + return 0; + + if (!TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, -1))) { + i_push_error(0, "write TIFF: setting rows per strip tag"); + return 0; + } + + in_row = mymalloc(im->xsize); + out_size = TIFFScanlineSize(tif); + out_row = (unsigned char *)_TIFFmalloc( out_size ); + + for (y = 0; y < im->ysize; ++y) { + i_gpal(im, 0, im->xsize, y, in_row); + memset(out_row, 0, out_size); + pack_4bit_to(out_row, in_row, im->xsize); + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + _TIFFfree(out_row); + i_push_error(0, "write TIFF: write scan line failed"); + return 0; + } + } + + myfree(in_row); + _TIFFfree(out_row); + + return 1; +} + +static int +set_direct_tags(TIFF *tif, i_img *im, uint16 compress, + uint16 bits_per_sample) { + uint16 extras = EXTRASAMPLE_ASSOCALPHA; + uint16 extra_count = im->channels == 2 || im->channels == 4; + uint16 photometric = im->channels >= 3 ? + PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK; + + if (!set_base_tags(tif, im, compress, photometric, bits_per_sample, + im->channels)) { + return 0; + } + + if (extra_count) { + if (!TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, extra_count, &extras)) { + i_push_error(0, "write TIFF: setting extra samples tag"); + return 0; + } + } + + if (compress == COMPRESSION_JPEG) { + int jpeg_quality; + if (i_tags_get_int(&im->tags, "tiff_jpegquality", 0, &jpeg_quality) + && jpeg_quality >= 0 && jpeg_quality <= 100) { + if (!TIFFSetField(tif, TIFFTAG_JPEGQUALITY, jpeg_quality)) { + i_push_error(0, "write TIFF: setting jpeg quality pseudo-tag"); + return 0; + } + } + } + + return 1; +} + +static int +write_one_32(TIFF *tif, i_img *im) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + unsigned *in_row; + size_t out_size; + uint32 *out_row; + int y; + size_t sample_count = im->xsize * im->channels; + size_t sample_index; + + mm_log((1, "tiff - write_one_32(tif %p, im %p)\n", tif, im)); + + /* only 8 and 12 bit samples are supported by jpeg compression */ + if (compress == COMPRESSION_JPEG) + compress = COMPRESSION_PACKBITS; + + if (!set_direct_tags(tif, im, compress, 32)) + return 0; + + in_row = mymalloc(sample_count * sizeof(unsigned)); + out_size = TIFFScanlineSize(tif); + out_row = (uint32 *)_TIFFmalloc( out_size ); + + for (y = 0; y < im->ysize; ++y) { + if (i_gsamp_bits(im, 0, im->xsize, y, in_row, NULL, im->channels, 32) <= 0) { + i_push_error(0, "Cannot read 32-bit samples"); + return 0; + } + for (sample_index = 0; sample_index < sample_count; ++sample_index) + out_row[sample_index] = in_row[sample_index]; + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + myfree(in_row); + _TIFFfree(out_row); + i_push_error(0, "write TIFF: write scan line failed"); + return 0; + } + } + + myfree(in_row); + _TIFFfree(out_row); + + return 1; +} + +static int +write_one_16(TIFF *tif, i_img *im) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + unsigned *in_row; + size_t out_size; + uint16 *out_row; + int y; + size_t sample_count = im->xsize * im->channels; + size_t sample_index; + + mm_log((1, "tiff - write_one_16(tif %p, im %p)\n", tif, im)); + + /* only 8 and 12 bit samples are supported by jpeg compression */ + if (compress == COMPRESSION_JPEG) + compress = COMPRESSION_PACKBITS; + + if (!set_direct_tags(tif, im, compress, 16)) + return 0; + + in_row = mymalloc(sample_count * sizeof(unsigned)); + out_size = TIFFScanlineSize(tif); + out_row = (uint16 *)_TIFFmalloc( out_size ); + + for (y = 0; y < im->ysize; ++y) { + if (i_gsamp_bits(im, 0, im->xsize, y, in_row, NULL, im->channels, 16) <= 0) { + i_push_error(0, "Cannot read 16-bit samples"); + return 0; + } + for (sample_index = 0; sample_index < sample_count; ++sample_index) + out_row[sample_index] = in_row[sample_index]; + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + myfree(in_row); + _TIFFfree(out_row); + i_push_error(0, "write TIFF: write scan line failed"); + return 0; + } + } + + myfree(in_row); + _TIFFfree(out_row); + + return 1; +} + +static int +write_one_8(TIFF *tif, i_img *im) { + uint16 compress = get_compression(im, COMPRESSION_PACKBITS); + size_t out_size; + unsigned char *out_row; + int y; + size_t sample_count = im->xsize * im->channels; + + mm_log((1, "tiff - write_one_8(tif %p, im %p)\n", tif, im)); + + if (!set_direct_tags(tif, im, compress, 8)) + return 0; + + out_size = TIFFScanlineSize(tif); + if (out_size < sample_count) + out_size = sample_count; + out_row = (unsigned char *)_TIFFmalloc( out_size ); + + for (y = 0; y < im->ysize; ++y) { + if (i_gsamp(im, 0, im->xsize, y, out_row, NULL, im->channels) <= 0) { + i_push_error(0, "Cannot read 8-bit samples"); + return 0; + } + if (TIFFWriteScanline(tif, out_row, y, 0) < 0) { + _TIFFfree(out_row); + i_push_error(0, "write TIFF: write scan line failed"); + return 0; + } + } + _TIFFfree(out_row); + + return 1; +} + +static int +i_writetiff_low(TIFF *tif, i_img *im) { + uint32 width, height; + uint16 channels; + int zero_is_white; + + width = im->xsize; + height = im->ysize; + channels = im->channels; + + mm_log((1, "i_writetiff_low: width=%d, height=%d, channels=%d, bits=%d\n", width, height, channels, im->bits)); + if (im->type == i_palette_type) { + mm_log((1, "i_writetiff_low: paletted, colors=%d\n", i_colorcount(im))); + } + + if (i_img_is_monochrome(im, &zero_is_white)) { + if (!write_one_bilevel(tif, im, zero_is_white)) + return 0; + } + else if (im->type == i_palette_type) { + if (i_colorcount(im) <= 16) { + if (!write_one_paletted4(tif, im)) + return 0; + } + else { + if (!write_one_paletted8(tif, im)) + return 0; + } + } + else if (im->bits > 16) { + if (!write_one_32(tif, im)) + return 0; + } + else if (im->bits > 8) { + if (!write_one_16(tif, im)) + return 0; + } + else { + if (!write_one_8(tif, im)) + return 0; + } + + if (!save_tiff_tags(tif, im)) + return 0; + + 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; + TIFFErrorHandler old_handler; + int i; + + old_handler = TIFFSetErrorHandler(error_handler); + + 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_multi_wiol: Unable to open tif file for writing\n")); + i_push_error(0, "Could not create TIFF object"); + TIFFSetErrorHandler(old_handler); + return 0; + } + + for (i = 0; i < count; ++i) { + if (!i_writetiff_low(tif, imgs[i])) { + TIFFClose(tif); + TIFFSetErrorHandler(old_handler); + return 0; + } + + if (!TIFFWriteDirectory(tif)) { + i_push_error(0, "Cannot write TIFF directory"); + TIFFClose(tif); + TIFFSetErrorHandler(old_handler); return 0; } } @@ -1194,32 +1520,1034 @@ static int save_tiff_tags(TIFF *tif, i_img *im) { } -/* -=item expand_4bit_hl(buf, count) +static void +unpack_4bit_to(unsigned char *dest, const unsigned char *src, + int src_byte_count) { + while (src_byte_count > 0) { + *dest++ = *src >> 4; + *dest++ = *src++ & 0xf; + --src_byte_count; + } +} -Expands 4-bit/entry packed data into 1 byte/entry. +static void pack_4bit_to(unsigned char *dest, const unsigned char *src, + int pixel_count) { + int i = 0; + while (i < pixel_count) { + if ((i & 1) == 0) { + *dest = *src++ << 4; + } + else { + *dest++ |= *src++; + } + ++i; + } +} -buf must contain count bytes to be expanded and have 2*count bytes total -space. +static i_img * +make_rgb(TIFF *tif, int width, int height, int *alpha_chan) { + uint16 photometric; + uint16 channels, in_channels; + uint16 extra_count; + uint16 *extras; -The data is expanded in place. + TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &in_channels); + TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric); -=cut -*/ + switch (photometric) { + case PHOTOMETRIC_SEPARATED: + channels = 3; + break; + + case PHOTOMETRIC_MINISWHITE: + case PHOTOMETRIC_MINISBLACK: + /* the TIFF RGBA functions expand single channel grey into RGB, + so reduce it, we move the alpha channel into the right place + if needed */ + channels = 1; + break; -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; + default: + channels = 3; + break; + } + /* TIFF images can have more than one alpha channel, but Imager can't + this ignores the possibility of 2 channel images with 2 alpha, + but there's not much I can do about that */ + *alpha_chan = 0; + if (TIFFGetField(tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras) + && extra_count) { + *alpha_chan = channels++; } + + return i_img_8_new(width, height, channels); } -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; +static i_img * +read_one_rgb_lines(TIFF *tif, int width, int height, int allow_incomplete) { + i_img *im; + uint32* raster = NULL; + uint32 rowsperstrip, row; + i_color *line_buf; + int alpha_chan; + int rc; + + im = make_rgb(tif, width, height, &alpha_chan); + if (!im) + return NULL; + + 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; + } + + line_buf = mymalloc(sizeof(i_color) * width); + + for( row = 0; row < height; row += rowsperstrip ) { + uint32 newrows, i_row; + + if (!TIFFReadRGBAStrip(tif, row, raster)) { + if (allow_incomplete) { + i_tags_setn(&im->tags, "i_lines_read", row); + i_tags_setn(&im->tags, "i_incomplete", 1); + break; + } + else { + i_push_error(0, "could not read TIFF image strip"); + _TIFFfree(raster); + i_img_destroy(im); + return NULL; + } + } + + 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; + i_color *outp = line_buf; + + for(x = 0; xrgba.r = TIFFGetR(temp); + outp->rgba.g = TIFFGetG(temp); + outp->rgba.b = TIFFGetB(temp); + + if (alpha_chan) { + /* the libtiff RGBA code expands greyscale into RGBA, so put the + alpha in the right place and scale it */ + int ch; + outp->channel[alpha_chan] = TIFFGetA(temp); + if (outp->channel[alpha_chan]) { + for (ch = 0; ch < alpha_chan; ++ch) { + outp->channel[ch] = outp->channel[ch] * 255 / outp->channel[alpha_chan]; + } + } + } + + outp++; + } + i_plin(im, 0, width, i_row+row, line_buf); + } } + + myfree(line_buf); + _TIFFfree(raster); + + return im; +} + +/* adapted from libtiff + + libtiff's TIFFReadRGBATile succeeds even when asked to read an + invalid tile, which means we have no way of knowing whether the data + we received from it is valid or not. + + So the caller here has set stoponerror to 1 so that + TIFFRGBAImageGet() will fail. + + read_one_rgb_tiled() then takes that into account for i_incomplete + or failure. + */ +static int +myTIFFReadRGBATile(TIFFRGBAImage *img, uint32 col, uint32 row, uint32 * raster) + +{ + int ok; + uint32 tile_xsize, tile_ysize; + uint32 read_xsize, read_ysize; + uint32 i_row; + + /* + * Verify that our request is legal - on a tile file, and on a + * tile boundary. + */ + + TIFFGetFieldDefaulted(img->tif, TIFFTAG_TILEWIDTH, &tile_xsize); + TIFFGetFieldDefaulted(img->tif, TIFFTAG_TILELENGTH, &tile_ysize); + if( (col % tile_xsize) != 0 || (row % tile_ysize) != 0 ) + { + i_push_errorf(0, "Row/col passed to myTIFFReadRGBATile() must be top" + "left corner of a tile."); + return 0; + } + + /* + * The TIFFRGBAImageGet() function doesn't allow us to get off the + * edge of the image, even to fill an otherwise valid tile. So we + * figure out how much we can read, and fix up the tile buffer to + * a full tile configuration afterwards. + */ + + if( row + tile_ysize > img->height ) + read_ysize = img->height - row; + else + read_ysize = tile_ysize; + + if( col + tile_xsize > img->width ) + read_xsize = img->width - col; + else + read_xsize = tile_xsize; + + /* + * Read the chunk of imagery. + */ + + img->row_offset = row; + img->col_offset = col; + + ok = TIFFRGBAImageGet(img, raster, read_xsize, read_ysize ); + + /* + * If our read was incomplete we will need to fix up the tile by + * shifting the data around as if a full tile of data is being returned. + * + * This is all the more complicated because the image is organized in + * bottom to top format. + */ + + if( read_xsize == tile_xsize && read_ysize == tile_ysize ) + return( ok ); + + for( i_row = 0; i_row < read_ysize; i_row++ ) { + memmove( raster + (tile_ysize - i_row - 1) * tile_xsize, + raster + (read_ysize - i_row - 1) * read_xsize, + read_xsize * sizeof(uint32) ); + _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize+read_xsize, + 0, sizeof(uint32) * (tile_xsize - read_xsize) ); + } + + for( i_row = read_ysize; i_row < tile_ysize; i_row++ ) { + _TIFFmemset( raster + (tile_ysize - i_row - 1) * tile_xsize, + 0, sizeof(uint32) * tile_xsize ); + } + + return (ok); +} + +static i_img * +read_one_rgb_tiled(TIFF *tif, int width, int height, int allow_incomplete) { + i_img *im; + uint32* raster = NULL; + int ok = 1; + uint32 row, col; + uint32 tile_width, tile_height; + unsigned long pixels = 0; + char emsg[1024] = ""; + TIFFRGBAImage img; + i_color *line; + int alpha_chan; + + im = make_rgb(tif, width, height, &alpha_chan); + if (!im) + return NULL; + + if (!TIFFRGBAImageOK(tif, emsg) + || !TIFFRGBAImageBegin(&img, tif, 1, emsg)) { + i_push_error(0, emsg); + i_img_destroy(im); + return( 0 ); + } + + 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"); + TIFFRGBAImageEnd(&img); + return NULL; + } + line = mymalloc(tile_width * sizeof(i_color)); + + for( row = 0; row < height; row += tile_height ) { + for( col = 0; col < width; col += tile_width ) { + + /* Read the tile into an RGBA array */ + if (myTIFFReadRGBATile(&img, col, row, raster)) { + uint32 i_row, x; + uint32 newrows = (row+tile_height > height) ? height-row : tile_height; + uint32 newcols = (col+tile_width > width ) ? width-col : tile_width; + + mm_log((1, "i_readtiff_wiol: tile(%d, %d) newcols=%d newrows=%d\n", col, row, newcols, newrows)); + for( i_row = 0; i_row < newrows; i_row++ ) { + i_color *outp = line; + for(x = 0; x < newcols; x++) { + uint32 temp = raster[x+tile_width*(tile_height-i_row-1)]; + outp->rgba.r = TIFFGetR(temp); + outp->rgba.g = TIFFGetG(temp); + outp->rgba.b = TIFFGetB(temp); + outp->rgba.a = TIFFGetA(temp); + + if (alpha_chan) { + /* the libtiff RGBA code expands greyscale into RGBA, so put the + alpha in the right place and scale it */ + int ch; + outp->channel[alpha_chan] = TIFFGetA(temp); + + if (outp->channel[alpha_chan]) { + for (ch = 0; ch < alpha_chan; ++ch) { + outp->channel[ch] = outp->channel[ch] * 255 / outp->channel[alpha_chan]; + } + } + } + + ++outp; + } + i_plin(im, col, col+newcols, row+i_row, line); + } + pixels += newrows * newcols; + } + else { + if (allow_incomplete) { + ok = 0; + } + else { + goto error; + } + } + } + } + + if (!ok) { + if (pixels == 0) { + i_push_error(0, "TIFF: No image data could be read from the image"); + goto error; + } + + /* incomplete image */ + i_tags_setn(&im->tags, "i_incomplete", 1); + i_tags_setn(&im->tags, "i_lines_read", pixels / width); + } + + myfree(line); + TIFFRGBAImageEnd(&img); + _TIFFfree(raster); + + return im; + + error: + myfree(line); + _TIFFfree(raster); + TIFFRGBAImageEnd(&img); + i_img_destroy(im); + return NULL; +} + +char const * +i_tiff_libversion(void) { + return TIFFGetVersion(); +} + +static int +setup_paletted(read_state_t *state) { + uint16 *maps[3]; + int i, ch; + int color_count = 1 << state->bits_per_sample; + + state->img = i_img_pal_new(state->width, state->height, 3, 256); + if (!state->img) + return 0; + + /* setup the color map */ + if (!TIFFGetField(state->tif, TIFFTAG_COLORMAP, maps+0, maps+1, maps+2)) { + i_push_error(0, "Cannot get colormap for paletted image"); + i_img_destroy(state->img); + return 0; + } + for (i = 0; i < color_count; ++i) { + i_color c; + for (ch = 0; ch < 3; ++ch) { + c.channel[ch] = Sample16To8(maps[ch][i]); + } + i_addcolors(state->img, &c, 1); + } + + return 1; +} + +static int +tile_contig_getter(read_state_t *state, read_putter_t putter) { + uint32 tile_width, tile_height; + uint32 this_tile_height, this_tile_width; + uint32 rows_left, cols_left; + uint32 x, y; + + state->raster = _TIFFmalloc(TIFFTileSize(state->tif)); + if (!state->raster) { + i_push_error(0, "tiff: Out of memory allocating tile buffer"); + return 0; + } + + TIFFGetField(state->tif, TIFFTAG_TILEWIDTH, &tile_width); + TIFFGetField(state->tif, TIFFTAG_TILELENGTH, &tile_height); + rows_left = state->height; + for (y = 0; y < state->height; y += this_tile_height) { + this_tile_height = rows_left > tile_height ? tile_height : rows_left; + + cols_left = state->width; + for (x = 0; x < state->width; x += this_tile_width) { + this_tile_width = cols_left > tile_width ? tile_width : cols_left; + + if (TIFFReadTile(state->tif, + state->raster, + x, y, 0, 0) < 0) { + if (!state->allow_incomplete) { + return 0; + } + } + else { + putter(state, x, y, this_tile_width, this_tile_height, tile_width - this_tile_width); + } + + cols_left -= this_tile_width; + } + + rows_left -= this_tile_height; + } + + return 1; +} + +static int +strip_contig_getter(read_state_t *state, read_putter_t putter) { + uint32 rows_per_strip; + tsize_t strip_size = TIFFStripSize(state->tif); + uint32 y, strip_rows, rows_left; + + state->raster = _TIFFmalloc(strip_size); + if (!state->raster) { + i_push_error(0, "tiff: Out of memory allocating strip buffer"); + return 0; + } + + TIFFGetFieldDefaulted(state->tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip); + rows_left = state->height; + for (y = 0; y < state->height; y += strip_rows) { + strip_rows = rows_left > rows_per_strip ? rows_per_strip : rows_left; + if (TIFFReadEncodedStrip(state->tif, + TIFFComputeStrip(state->tif, y, 0), + state->raster, + strip_size) < 0) { + if (!state->allow_incomplete) + return 0; + } + else { + putter(state, 0, y, state->width, strip_rows, 0); + } + rows_left -= strip_rows; + } + + return 1; +} + +static int +paletted_putter8(read_state_t *state, int x, int y, int width, int height, int extras) { + unsigned char *p = state->raster; + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + i_ppal(state->img, x, x + width, y, p); + p += width + extras; + --height; + ++y; + } + + return 1; +} + +static int +paletted_putter4(read_state_t *state, int x, int y, int width, int height, int extras) { + uint32 img_line_size = (width + 1) / 2; + uint32 skip_line_size = (width + extras + 1) / 2; + unsigned char *p = state->raster; + + if (!state->line_buf) + state->line_buf = mymalloc(state->width); + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + unpack_4bit_to(state->line_buf, p, img_line_size); + i_ppal(state->img, x, x + width, y, state->line_buf); + p += skip_line_size; + --height; + ++y; + } + + return 1; +} + +static void +rgb_channels(read_state_t *state, int *out_channels) { + uint16 extra_count; + uint16 *extras; + + /* safe defaults */ + *out_channels = 3; + state->alpha_chan = 0; + state->scale_alpha = 0; + + /* plain RGB */ + if (state->samples_per_pixel == 3) + return; + + if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) { + mm_log((1, "tiff: samples != 3 but no extra samples tag\n")); + return; + } + + if (!extra_count) { + mm_log((1, "tiff: samples != 3 but no extra samples listed")); + return; + } + + ++*out_channels; + state->alpha_chan = 3; + switch (*extras) { + case EXTRASAMPLE_UNSPECIFIED: + case EXTRASAMPLE_ASSOCALPHA: + state->scale_alpha = 1; + break; + + case EXTRASAMPLE_UNASSALPHA: + state->scale_alpha = 0; + break; + + default: + mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n", + *extras)); + state->scale_alpha = 1; + break; + } + mm_log((1, "tiff alpha channel %d scale %d\n", state->alpha_chan, state->scale_alpha)); +} + +static void +grey_channels(read_state_t *state, int *out_channels) { + uint16 extra_count; + uint16 *extras; + + /* safe defaults */ + *out_channels = 1; + state->alpha_chan = 0; + state->scale_alpha = 0; + + /* plain grey */ + if (state->samples_per_pixel == 1) + return; + + if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) { + mm_log((1, "tiff: samples != 1 but no extra samples tag\n")); + return; + } + + if (!extra_count) { + mm_log((1, "tiff: samples != 1 but no extra samples listed")); + return; + } + + ++*out_channels; + state->alpha_chan = 1; + switch (*extras) { + case EXTRASAMPLE_UNSPECIFIED: + case EXTRASAMPLE_ASSOCALPHA: + state->scale_alpha = 1; + break; + + case EXTRASAMPLE_UNASSALPHA: + state->scale_alpha = 0; + break; + + default: + mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n", + *extras)); + state->scale_alpha = 1; + break; + } +} + +static int +setup_16_rgb(read_state_t *state) { + int out_channels; + + rgb_channels(state, &out_channels); + + state->img = i_img_16_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels); + + return 1; +} + +static int +setup_16_grey(read_state_t *state) { + int out_channels; + + grey_channels(state, &out_channels); + + state->img = i_img_16_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels); + + return 1; +} + +static int +putter_16(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + uint16 *p = state->raster; + int out_chan = state->img->channels; + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + int ch; + unsigned *outp = state->line_buf; + + for (i = 0; i < width; ++i) { + for (ch = 0; ch < out_chan; ++ch) { + outp[ch] = p[ch]; + } + if (state->alpha_chan && state->scale_alpha && outp[state->alpha_chan]) { + for (ch = 0; ch < state->alpha_chan; ++ch) { + int result = 0.5 + (outp[ch] * 65535.0 / outp[state->alpha_chan]); + outp[ch] = CLAMP16(result); + } + } + p += state->samples_per_pixel; + outp += out_chan; + } + + i_psamp_bits(state->img, x, x + width, y, state->line_buf, NULL, out_chan, 16); + + p += row_extras * state->samples_per_pixel; + --height; + ++y; + } + + return 1; +} + +static int +setup_8_rgb(read_state_t *state) { + int out_channels; + + rgb_channels(state, &out_channels); + + state->img = i_img_8_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(unsigned) * state->width * out_channels); + + return 1; +} + +static int +setup_8_grey(read_state_t *state) { + int out_channels; + + grey_channels(state, &out_channels); + + state->img = i_img_8_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(i_color) * state->width * out_channels); + + return 1; +} + +static int +putter_8(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + unsigned char *p = state->raster; + int out_chan = state->img->channels; + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + int ch; + i_color *outp = state->line_buf; + + for (i = 0; i < width; ++i) { + for (ch = 0; ch < out_chan; ++ch) { + outp->channel[ch] = p[ch]; + } + if (state->alpha_chan && state->scale_alpha + && outp->channel[state->alpha_chan]) { + for (ch = 0; ch < state->alpha_chan; ++ch) { + int result = (outp->channel[ch] * 255 + 127) / outp->channel[state->alpha_chan]; + + outp->channel[ch] = CLAMP8(result); + } + } + p += state->samples_per_pixel; + outp++; + } + + i_plin(state->img, x, x + width, y, state->line_buf); + + p += row_extras * state->samples_per_pixel; + --height; + ++y; + } + + return 1; +} + +static int +setup_32_rgb(read_state_t *state) { + int out_channels; + + rgb_channels(state, &out_channels); + + state->img = i_img_double_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(i_fcolor) * state->width); + + return 1; +} + +static int +setup_32_grey(read_state_t *state) { + int out_channels; + + grey_channels(state, &out_channels); + + state->img = i_img_double_new(state->width, state->height, out_channels); + if (!state->img) + return 0; + state->line_buf = mymalloc(sizeof(i_fcolor) * state->width); + + return 1; +} + +static int +putter_32(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + uint32 *p = state->raster; + int out_chan = state->img->channels; + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + int ch; + i_fcolor *outp = state->line_buf; + + for (i = 0; i < width; ++i) { + for (ch = 0; ch < out_chan; ++ch) { + outp->channel[ch] = p[ch] / 4294967295.0; + } + if (state->alpha_chan && state->scale_alpha && outp->channel[state->alpha_chan]) { + for (ch = 0; ch < state->alpha_chan; ++ch) + outp->channel[ch] /= outp->channel[state->alpha_chan]; + } + p += state->samples_per_pixel; + outp++; + } + + i_plinf(state->img, x, x + width, y, state->line_buf); + + p += row_extras * state->samples_per_pixel; + --height; + ++y; + } + + return 1; +} + +static int +setup_bilevel(read_state_t *state) { + i_color black, white; + state->img = i_img_pal_new(state->width, state->height, 1, 256); + if (!state->img) + return 0; + black.channel[0] = black.channel[1] = black.channel[2] = + black.channel[3] = 0; + white.channel[0] = white.channel[1] = white.channel[2] = + white.channel[3] = 255; + if (state->photometric == PHOTOMETRIC_MINISBLACK) { + i_addcolors(state->img, &black, 1); + i_addcolors(state->img, &white, 1); + } + else { + i_addcolors(state->img, &white, 1); + i_addcolors(state->img, &black, 1); + } + state->line_buf = mymalloc(state->width); + + return 1; +} + +static int +putter_bilevel(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + unsigned char *line_in = state->raster; + size_t line_size = (width + row_extras + 7) / 8; + + /* tifflib returns the bits in MSB2LSB order even when the file is + in LSB2MSB, so we only need to handle MSB2LSB */ + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + unsigned char *outp = state->line_buf; + unsigned char *inp = line_in; + unsigned mask = 0x80; + + for (i = 0; i < width; ++i) { + *outp++ = *inp & mask ? 1 : 0; + mask >>= 1; + if (!mask) { + ++inp; + mask = 0x80; + } + } + + i_ppal(state->img, x, x + width, y, state->line_buf); + + line_in += line_size; + --height; + ++y; + } + + return 1; +} + +static void +cmyk_channels(read_state_t *state, int *out_channels) { + uint16 extra_count; + uint16 *extras; + + /* safe defaults */ + *out_channels = 3; + state->alpha_chan = 0; + state->scale_alpha = 0; + + /* plain CMYK */ + if (state->samples_per_pixel == 4) + return; + + if (!TIFFGetField(state->tif, TIFFTAG_EXTRASAMPLES, &extra_count, &extras)) { + mm_log((1, "tiff: CMYK samples != 4 but no extra samples tag\n")); + return; + } + + if (!extra_count) { + mm_log((1, "tiff: CMYK samples != 4 but no extra samples listed")); + return; + } + + ++*out_channels; + state->alpha_chan = 4; + switch (*extras) { + case EXTRASAMPLE_UNSPECIFIED: + case EXTRASAMPLE_ASSOCALPHA: + state->scale_alpha = 1; + break; + + case EXTRASAMPLE_UNASSALPHA: + state->scale_alpha = 0; + break; + + default: + mm_log((1, "tiff: unknown extra sample type %d, treating as assoc alpha\n", + *extras)); + state->scale_alpha = 1; + break; + } +} + +static int +setup_cmyk8(read_state_t *state) { + int channels; + + cmyk_channels(state, &channels); + state->img = i_img_8_new(state->width, state->height, channels); + + state->line_buf = mymalloc(sizeof(i_color) * state->width); + + return 1; +} + +static int +putter_cmyk8(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + unsigned char *p = state->raster; + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + int ch; + i_color *outp = state->line_buf; + + for (i = 0; i < width; ++i) { + unsigned char c, m, y, k; + c = p[0]; + m = p[1]; + y = p[2]; + k = 255 - p[3]; + outp->rgba.r = (k * (255 - c)) / 255; + outp->rgba.g = (k * (255 - m)) / 255; + outp->rgba.b = (k * (255 - y)) / 255; + if (state->alpha_chan) { + outp->rgba.a = p[state->alpha_chan]; + if (state->scale_alpha + && outp->rgba.a) { + for (ch = 0; ch < 3; ++ch) { + int result = (outp->channel[ch] * 255 + 127) / outp->rgba.a; + outp->channel[ch] = CLAMP8(result); + } + } + } + p += state->samples_per_pixel; + outp++; + } + + i_plin(state->img, x, x + width, y, state->line_buf); + + p += row_extras * state->samples_per_pixel; + --height; + ++y; + } + + return 1; +} + +static int +setup_cmyk16(read_state_t *state) { + int channels; + + cmyk_channels(state, &channels); + state->img = i_img_16_new(state->width, state->height, channels); + + state->line_buf = mymalloc(sizeof(unsigned) * state->width * channels); + + return 1; +} + +static int +putter_cmyk16(read_state_t *state, int x, int y, int width, int height, + int row_extras) { + uint16 *p = state->raster; + int out_chan = state->img->channels; + + mm_log((4, "putter_cmyk16(%p, %d, %d, %d, %d, %d)\n", x, y, width, height, row_extras)); + + state->pixels_read += (unsigned long) width * height; + while (height > 0) { + int i; + int ch; + unsigned *outp = state->line_buf; + + for (i = 0; i < width; ++i) { + unsigned c, m, y, k; + c = p[0]; + m = p[1]; + y = p[2]; + k = 65535 - p[3]; + outp[0] = (k * (65535U - c)) / 65535U; + outp[1] = (k * (65535U - m)) / 65535U; + outp[2] = (k * (65535U - y)) / 65535U; + if (state->alpha_chan) { + outp[3] = p[state->alpha_chan]; + if (state->scale_alpha + && outp[3]) { + for (ch = 0; ch < 3; ++ch) { + int result = (outp[ch] * 65535 + 32767) / outp[3]; + outp[3] = CLAMP16(result); + } + } + } + p += state->samples_per_pixel; + outp += out_chan; + } + + i_psamp_bits(state->img, x, x + width, y, state->line_buf, NULL, out_chan, 16); + + p += row_extras * state->samples_per_pixel; + --height; + ++y; + } + + return 1; +} + +/* + + Older versions of tifflib we support don't define this, so define it + ourselves. + + If you want this detection to do anything useful, use a newer + release of tifflib. + + */ +#if TIFFLIB_VERSION < 20031121 + +int +TIFFIsCODECConfigured(uint16 scheme) { + switch (scheme) { + /* these schemes are all shipped with tifflib */ + case COMPRESSION_NONE: + case COMPRESSION_PACKBITS: + case COMPRESSION_CCITTRLE: + case COMPRESSION_CCITTRLEW: + case COMPRESSION_CCITTFAX3: + case COMPRESSION_CCITTFAX4: + return 1; + + /* these require external library support */ + default: + case COMPRESSION_JPEG: + case COMPRESSION_LZW: + case COMPRESSION_DEFLATE: + case COMPRESSION_ADOBE_DEFLATE: + return 0; + } +} + +#endif + +static int +myTIFFIsCODECConfigured(uint16 scheme) { +#if TIFFLIB_VERSION < 20040724 + if (scheme == COMPRESSION_LZW) + return 0; +#endif + + return TIFFIsCODECConfigured(scheme); } /* @@ -1227,7 +2555,7 @@ static void pack_4bit_hl(unsigned char *buf, int count) { =head1 AUTHOR -Arnar M. Hrafnkelsson +Arnar M. Hrafnkelsson , Tony Cook =head1 SEE ALSO