From 5a422aba704c375a307a902bafe658342e209906 Mon Sep 17 00:00:00 2001 From: scuri Date: Fri, 17 Oct 2008 06:10:15 +0000 Subject: First commit - moving from LuaForge to SourceForge --- src/im_format_tiff.cpp | 1421 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1421 insertions(+) create mode 100644 src/im_format_tiff.cpp (limited to 'src/im_format_tiff.cpp') diff --git a/src/im_format_tiff.cpp b/src/im_format_tiff.cpp new file mode 100644 index 0000000..98467f9 --- /dev/null +++ b/src/im_format_tiff.cpp @@ -0,0 +1,1421 @@ +/** \file + * \brief TIFF - Tagged Image File Format + * + * See Copyright Notice in im_lib.h + * See libTIFF Copyright Notice in tiff.h + * $Id: im_format_tiff.cpp,v 1.1 2008/10/17 06:10:16 scuri Exp $ + */ + +#include "im_format.h" +#include "im_util.h" +#include "im_format_all.h" +#include "im_counter.h" + +#include "tiffiop.h" + +#include +#include +#include +#include + + +#define TIFFTAG_GEOPIXELSCALE 33550 +#define TIFFTAG_INTERGRAPH_MATRIX 33920 +#define TIFFTAG_GEOTIEPOINTS 33922 +#define TIFFTAG_GEOTRANSMATRIX 34264 +#define TIFFTAG_GEOKEYDIRECTORY 34735 +#define TIFFTAG_GEODOUBLEPARAMS 34736 +#define TIFFTAG_GEOASCIIPARAMS 34737 + +#define TIFFTAG_CFAREPEATPATTERNDIM 33421 /* dimensions of CFA pattern */ +#define TIFFTAG_CFAPATTERN 33422 /* color filter array pattern */ +#define PHOTOMETRIC_CFA 32803 /* color filter array */ +#define PHOTOMETRIC_LINEARRAW 34892 + +static const TIFFFieldInfo iTiffFieldInfo[] = +{ + /* missing in libTIFF (fixed in libtiff 4.0) */ + { EXIFTAG_COLORSPACE, 1, 1, TIFF_SHORT, FIELD_CUSTOM, TRUE, FALSE, "ColorSpace" }, + + /* Patch from Dave Coffin (Used for DNG) */ + { TIFFTAG_WHITELEVEL, -2, -1, TIFF_LONG, FIELD_CUSTOM, 0, 1, "WhiteLevel" }, + { TIFFTAG_WHITELEVEL, -2, -1, TIFF_SHORT, FIELD_CUSTOM, 0, 1, "WhiteLevel" }, + { TIFFTAG_CFAREPEATPATTERNDIM, 2, 2, TIFF_SHORT, FIELD_CUSTOM, 0, 0, "CFARepeatPatternDim" }, + { TIFFTAG_CFAPATTERN, -1, -1, TIFF_BYTE, FIELD_CUSTOM, 0, 1, "CFAPattern" }, + + /* GeoTIFF Tags */ + { TIFFTAG_GEOPIXELSCALE, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoPixelScale" }, + { TIFFTAG_INTERGRAPH_MATRIX,-1,-1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "Intergraph TransformationMatrix" }, + { TIFFTAG_GEOTIEPOINTS, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTiePoints" }, + { TIFFTAG_GEOTRANSMATRIX, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoTransformationMatrix" }, + { TIFFTAG_GEOKEYDIRECTORY,-1,-1, TIFF_SHORT, FIELD_CUSTOM, TRUE, TRUE, "GeoKeyDirectory" }, + { TIFFTAG_GEODOUBLEPARAMS, -1,-1, TIFF_DOUBLE, FIELD_CUSTOM, TRUE, TRUE, "GeoDoubleParams" }, + { TIFFTAG_GEOASCIIPARAMS, -1,-1, TIFF_ASCII, FIELD_CUSTOM, TRUE, FALSE, "GeoASCIIParams" } +}; + +#define IMTIFF_NUMCOMP 15 + +/* this list must be sorted because of bsearch */ +static uint16 iTIFFCompIdTable [IMTIFF_NUMCOMP] = +{ + COMPRESSION_NONE, + COMPRESSION_CCITTRLE, + COMPRESSION_CCITTFAX3, + COMPRESSION_CCITTFAX4, + COMPRESSION_LZW, + COMPRESSION_JPEG, + COMPRESSION_ADOBE_DEFLATE, + COMPRESSION_NEXT, + COMPRESSION_CCITTRLEW, + COMPRESSION_PACKBITS, + COMPRESSION_THUNDERSCAN, + COMPRESSION_PIXARLOG, + COMPRESSION_DEFLATE, + COMPRESSION_SGILOG, + COMPRESSION_SGILOG24 +}; + +static int iTIFFCompareCompID(const void *elem1, const void *elem2) +{ + const uint16 *tiff_comp_elem1 = (const uint16 *)elem1; + const uint16 *tiff_comp_elem2 = (const uint16 *)elem2; + + if (*tiff_comp_elem1 > *tiff_comp_elem2) + return 1; + + if (*tiff_comp_elem1 < *tiff_comp_elem2) + return -1; + + return 0; +} + +static int iTIFFGetCompIndex(uint16 Compression) +{ + if (Compression == COMPRESSION_OJPEG) + Compression = COMPRESSION_JPEG; + + uint16* comp_result = (uint16 *)bsearch(&Compression, iTIFFCompIdTable, sizeof(iTIFFCompIdTable)/sizeof(uint16), sizeof(uint16), iTIFFCompareCompID); + + if (comp_result == NULL) + { + return -1; + } + + return (comp_result - iTIFFCompIdTable); +} + +/* this list must follow iTIFFCompIdTable order */ +static const char* iTIFFCompTable[IMTIFF_NUMCOMP] = +{ + "NONE", + "CCITTRLE", + "CCITTFAX3", + "CCITTFAX4", + "LZW", + "JPEG", + "ADOBEDEFLATE", + "NEXT", + "CCITTRLEW", + "RLE", + "THUNDERSCAN", + "PIXARLOG", + "DEFLATE", + "SGILOG", + "SGILOG24" +}; + +static uint16 iTIFFCompFind(const char* compression) +{ + for(int i = 0; i < IMTIFF_NUMCOMP; i++) + { + if (imStrEqual(compression, iTIFFCompTable[i])) + return iTIFFCompIdTable[i]; + } + + return (uint16)-1; +} + +static uint16 iTIFFCompDefault(int color_space, int data_type) +{ + if (color_space == IM_BINARY) + return COMPRESSION_CCITTRLE; + + if (color_space == IM_MAP) + return COMPRESSION_PACKBITS; + + if (color_space == IM_YCBCR && data_type == IM_BYTE) + return COMPRESSION_JPEG; + + if (color_space == IM_XYZ) + return COMPRESSION_SGILOG; + + if (data_type >= IM_FLOAT) + return COMPRESSION_NONE; + + return COMPRESSION_LZW; +} + +static uint16 iTIFFCompCalc(const char* compression, int color_mode, int data_type) +{ + uint16 Compression; + if (compression[0] == 0) + Compression = iTIFFCompDefault(imColorModeSpace(color_mode), data_type); + else + Compression = iTIFFCompFind(compression); + + return Compression; +} + +static int iTIFFWriteTag(TIFF* tiff, int index, const char* name, int data_type, int count, const void* data) +{ + const TIFFFieldInfo *fld = TIFFFieldWithName(tiff, name); + (void)data_type; + (void)index; + if (fld) + { + if (fld->field_tag == TIFFTAG_EXIFIFD || /* offset */ + fld->field_tag == TIFFTAG_GPSIFD || + fld->field_tag == TIFFTAG_INTEROPERABILITYIFD || + fld->field_tag == TIFFTAG_SUBIFD || + fld->field_tag == TIFFTAG_COLORMAP || /* handled elsewhere */ + fld->field_tag == TIFFTAG_EXTRASAMPLES || + fld->field_tag == TIFFTAG_TRANSFERFUNCTION || + fld->field_tag == TIFFTAG_RESOLUTIONUNIT || + fld->field_tag == TIFFTAG_XRESOLUTION || + fld->field_tag == TIFFTAG_YRESOLUTION || + fld->field_tag == TIFFTAG_INKNAMES) + return 1; + + if (fld->field_passcount) + { + double* double_data = NULL; + + if (fld->field_type==TIFF_DOUBLE) + { + float* float_data = (float*)data; + double_data = new double [count]; + for (int p = 0; p < count; p++) + double_data[p] = float_data[p]; + data = double_data; + } + + if (fld->field_writecount == TIFF_VARIABLE2) + { + uint32 value_count = (uint32)count; + if (TIFFSetField(tiff, fld->field_tag, value_count, data) != 1) + return 1; + } + else + { + uint16 value_count = (uint16)count; + if (TIFFSetField(tiff, fld->field_tag, value_count, data) != 1) + return 1; + } + + if (fld->field_type==TIFF_DOUBLE) + delete [] double_data; + } + else + { + if (fld->field_tag == TIFFTAG_PAGENUMBER || + fld->field_tag == TIFFTAG_HALFTONEHINTS || + fld->field_tag == TIFFTAG_YCBCRSUBSAMPLING || + fld->field_tag == TIFFTAG_DOTRANGE) + { + // there are 2 separated ushort values + uint16* ushort_value = (uint16*)data; + TIFFSetField(tiff, fld->field_tag, ushort_value[0], ushort_value[1]); + return 1; + } + + if (count > 1 || fld->field_type == TIFF_ASCII) + TIFFSetField(tiff, fld->field_tag, data); + else + { + switch(fld->field_type) + { + case TIFF_UNDEFINED: + case TIFF_ASCII: + case TIFF_BYTE: + case TIFF_SBYTE: + { + imbyte* byte_data = (imbyte*)data; + TIFFSetField(tiff, fld->field_tag, *byte_data); + } + break; + case TIFF_SHORT: + case TIFF_SSHORT: + { + imushort* short_data = (imushort*)data; + TIFFSetField(tiff, fld->field_tag, *short_data); + } + break; + case TIFF_LONG: + case TIFF_SLONG: + { + int* long_data = (int*)data; + TIFFSetField(tiff, fld->field_tag, *long_data); + } + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + { + float* float_data = (float*)data; + TIFFSetField(tiff, fld->field_tag, *float_data); + } + break; + case TIFF_DOUBLE: + { + float* float_data = (float*)data; + TIFFSetField(tiff, fld->field_tag, (double)*float_data); + } + break; + default: + break; + } + } + } + } + + return 1; +} + +static void iTIFFWriteCustomTags(TIFF* tiff, imAttribTable* attrib_table) +{ + attrib_table->ForEach(tiff, (imAttribTableCallback)iTIFFWriteTag); +} + +static void iTIFFReadCustomTags(TIFF* tiff, imAttribTable* attrib_table) +{ + int i; + short tag_count; + + tag_count = (short) TIFFGetTagListCount(tiff); + for( i = 0; i < tag_count; i++ ) + { + ttag_t tag = TIFFGetTagListEntry(tiff, i); + const TIFFFieldInfo *fld; + + fld = TIFFFieldWithTag(tiff, tag); + if (fld == NULL) + continue; + + if (fld->field_tag == TIFFTAG_EXIFIFD || /* offset */ + fld->field_tag == TIFFTAG_GPSIFD || + fld->field_tag == TIFFTAG_INTEROPERABILITYIFD || + fld->field_tag == TIFFTAG_SUBIFD || + fld->field_tag == TIFFTAG_COLORMAP || /* handled elsewhere */ + fld->field_tag == TIFFTAG_EXTRASAMPLES || + fld->field_tag == TIFFTAG_TRANSFERFUNCTION || + fld->field_tag == TIFFTAG_RESOLUTIONUNIT || + fld->field_tag == TIFFTAG_XRESOLUTION || + fld->field_tag == TIFFTAG_YRESOLUTION || + fld->field_tag == TIFFTAG_INKNAMES) + continue; + + if (fld->field_tag == TIFFTAG_BLACKLEVEL || + fld->field_tag == TIFFTAG_DEFAULTCROPSIZE || + fld->field_tag == TIFFTAG_DEFAULTCROPORIGIN) + { + /* libTIFF bug. When reading custom tags there is an incorrect interpretation of the tag + that leads to return always type=RATIONAL for these tags. */ + continue; + } + + int data_type = -1, data_count = -1; + void* data = NULL; + + if (fld->field_passcount) + { + if (fld->field_readcount == TIFF_VARIABLE2) + { + uint32 value_count; + if (TIFFGetField(tiff, tag, &value_count, &data) != 1) + continue; + data_count = value_count; + } + else + { + uint16 value_count; + if (TIFFGetField(tiff, tag, &value_count, &data) != 1) + continue; + data_count = value_count; + } + + switch(fld->field_type) + { + case TIFF_UNDEFINED: + case TIFF_ASCII: + case TIFF_BYTE: + case TIFF_SBYTE: + data_type = IM_BYTE; + break; + case TIFF_SHORT: + case TIFF_SSHORT: + data_type = IM_USHORT; + break; + case TIFF_LONG: + case TIFF_SLONG: + data_type = IM_INT; + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + data_type = IM_FLOAT; + break; + case TIFF_DOUBLE: + { + double* double_data = (double*)data; + float* float_data = new float [data_count]; + for (int p = 0; p < data_count; p++) + float_data[p] = (float)double_data[p]; + attrib_table->Set(fld->field_name, IM_FLOAT, data_count, float_data); + delete [] float_data; + } + continue; + default: + continue; + } + + if (data && data_count > 0) + attrib_table->Set(fld->field_name, data_type, data_count, data); + } + else + { + data_count = fld->field_readcount; + + if (fld->field_tag == TIFFTAG_PAGENUMBER || + fld->field_tag == TIFFTAG_HALFTONEHINTS || + fld->field_tag == TIFFTAG_YCBCRSUBSAMPLING || + fld->field_tag == TIFFTAG_DOTRANGE) + { + // there are 2 separated ushort values + uint16 ushort_value[2]; + if (TIFFGetField(tiff, fld->field_tag, &ushort_value[0], &ushort_value[1])) + attrib_table->Set(fld->field_name, IM_USHORT, 2, ushort_value); + continue; + } + + switch(fld->field_type) + { + case TIFF_UNDEFINED: + case TIFF_BYTE: + case TIFF_SBYTE: + case TIFF_ASCII: + data_type = IM_BYTE; + break; + case TIFF_SHORT: + case TIFF_SSHORT: + data_type = IM_USHORT; + break; + case TIFF_LONG: + case TIFF_SLONG: + data_type = IM_INT; + break; + case TIFF_RATIONAL: + case TIFF_SRATIONAL: + case TIFF_FLOAT: + case TIFF_DOUBLE: + data_type = IM_FLOAT; + break; + default: + continue; + } + + if (fld->field_type == TIFF_ASCII || + fld->field_readcount == TIFF_VARIABLE || + fld->field_readcount == TIFF_VARIABLE2 || + fld->field_readcount == TIFF_SPP || + data_count > 1) + { + if (TIFFGetField(tiff, tag, &data) != 1) + continue; + + if (data) + { + if (fld->field_type == TIFF_ASCII && data_count == -1) + data_count = strlen((char*)data)+1; + + if (data_count > 0) + { + if (fld->field_type == TIFF_DOUBLE) + { + double* double_data = (double*)data; + float* float_data = new float [data_count]; + for (int p = 0; p < data_count; p++) + float_data[p] = (float)double_data[p]; + attrib_table->Set(fld->field_name, IM_FLOAT, data_count, float_data); + delete [] float_data; + } + else + attrib_table->Set(fld->field_name, data_type, data_count, data); + } + } + } + else if (data_count == 1) + { + data = malloc(imDataTypeSize(data_type)); + if (TIFFGetField(tiff, tag, data) == 1) + attrib_table->Set(fld->field_name, data_type, data_count, data); + free(data); + data = NULL; + } + } + } +} + +static void iTIFFReadAttributes(TIFF* tiff, imAttribTable* attrib_table) +{ + uint16 ResolutionUnit = RESUNIT_NONE; + TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &ResolutionUnit); + if (ResolutionUnit != RESUNIT_NONE) + { + float xres = 0, yres = 0; + + TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &xres); + TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &yres); + + if (xres != 0 && yres != 0) + { + if (ResolutionUnit == RESUNIT_INCH) + attrib_table->Set("ResolutionUnit", IM_BYTE, 4, "DPI"); + else + attrib_table->Set("ResolutionUnit", IM_BYTE, 4, "DPC"); + + attrib_table->Set("XResolution", IM_FLOAT, 1, (void*)&xres); + attrib_table->Set("YResolution", IM_FLOAT, 1, (void*)&yres); + } + } + + uint16 *transferfunction[3]; + if (TIFFGetField(tiff, TIFFTAG_TRANSFERFUNCTION, &transferfunction[0], &transferfunction[1], &transferfunction[2])) + { + uint16 SamplesPerPixel = 1, BitsPerSample = 1, ExtraSamples = 0, *SampleInfo; + TIFFGetFieldDefaulted(tiff, TIFFTAG_BITSPERSAMPLE, &BitsPerSample); + TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &ExtraSamples, &SampleInfo); + TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + + int num = (SamplesPerPixel - ExtraSamples) > 1 ? 3 : 1; + int count = 1L<Set("TransferFunction0", IM_USHORT, count, transferfunction[0]); + else + { + attrib_table->Set("TransferFunction0", IM_USHORT, count, transferfunction[0]); + attrib_table->Set("TransferFunction1", IM_USHORT, count, transferfunction[1]); + attrib_table->Set("TransferFunction2", IM_USHORT, count, transferfunction[2]); + } + } + + char *inknames; + if (TIFFGetField(tiff, TIFFTAG_INKNAMES, &inknames)) + { + // Ink names are separated by '0', so strlen will measure only the first string + uint16 numinks; + TIFFGetField(tiff, TIFFTAG_NUMBEROFINKS, &numinks); + int inknameslen = 0; + for (int k = 0; k < (int)numinks; k++) + inknameslen += strlen(inknames+inknameslen)+1; + attrib_table->Set("InkNames", IM_BYTE, inknameslen, inknames); + } + + iTIFFReadCustomTags(tiff, attrib_table); + + uint32 offset; + if (TIFFGetField(tiff, TIFFTAG_EXIFIFD, &offset)) + { + tdir_t cur_dir = TIFFCurrentDirectory(tiff); + + if (!TIFFReadEXIFDirectory(tiff, offset)) + { + TIFFSetDirectory(tiff, cur_dir); + return; + } + + iTIFFReadCustomTags(tiff, attrib_table); + TIFFSetDirectory(tiff, cur_dir); + } +} + +static void iTIFFWriteAttributes(TIFF* tiff, imAttribTable* attrib_table) +{ + char* res_unit = (char*)attrib_table->Get("ResolutionUnit"); + if (res_unit) + { + float* xres = (float*)attrib_table->Get("XResolution"); + float* yres = (float*)attrib_table->Get("YResolution"); + + if (xres && yres) + { + uint16 tiff_res_unit = RESUNIT_CENTIMETER; + if (imStrEqual(res_unit, "DPI")) + tiff_res_unit = RESUNIT_INCH; + + TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, tiff_res_unit); + TIFFSetField(tiff, TIFFTAG_XRESOLUTION, *xres); + TIFFSetField(tiff, TIFFTAG_YRESOLUTION, *yres); + } + } + + uint16 *transferfunction0 = (uint16*)attrib_table->Get("TransferFunction0"); + if (transferfunction0) + { + uint16 SamplesPerPixel = 1, ExtraSamples = 0, *SampleInfo; + TIFFGetFieldDefaulted(tiff, TIFFTAG_EXTRASAMPLES, &ExtraSamples, &SampleInfo); + TIFFGetFieldDefaulted(tiff, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + + int num = (SamplesPerPixel - ExtraSamples) > 1 ? 3 : 1; + if (num == 1) + TIFFSetField(tiff, TIFFTAG_TRANSFERFUNCTION, transferfunction0); + else + { + uint16 *transferfunction1 = (uint16*)attrib_table->Get("TransferFunction1"); + uint16 *transferfunction2 = (uint16*)attrib_table->Get("TransferFunction2"); + + if (transferfunction1 && transferfunction2) + TIFFSetField(tiff, TIFFTAG_TRANSFERFUNCTION, transferfunction0, transferfunction1, transferfunction2); + } + } + + char* inknames = (char*)attrib_table->Get("InkNames"); + if (inknames) + TIFFSetField(tiff, TIFFTAG_INKNAMES, inknames); + + int proflength; + const void* profdata = attrib_table->Get("ICCProfile", (int*)NULL, &proflength); + if (profdata) + TIFFSetField(tiff, TIFFTAG_ICCPROFILE, proflength, profdata); + + iTIFFWriteCustomTags(tiff, attrib_table); +} + +class imFormatTIFF: public imFormat +{ + TIFF* tiff; + int invert, // must invert black and white reference + cpx_int, // original data is a complex integer + lab_fix, // convert CIE Lab to unsigned + extra_sample_size, // eliminate extra samples if more than one + sample_size, + start_plane; // first band to read in a multiband image + + void** tile_buf; + int tile_buf_count, tile_width, tile_height, start_row, tile_line_size, tile_line_raw_size; + + int ReadTileline(void* line_buffer, int row, int plane); + +public: + imFormatTIFF() + :imFormat("TIFF", + "Tagged Image File Format", + "*.tif;*.tiff;", + iTIFFCompTable, + IMTIFF_NUMCOMP, + 1) + {} + ~imFormatTIFF() {} + + int Open(const char* file_name); + int New(const char* file_name); + void Close(); + void* Handle(int index); + int ReadImageInfo(int index); + int ReadImageData(void* data); + int WriteImageInfo(); + int WriteImageData(void* data); + int CanWrite(const char* compression, int color_mode, int data_type) const; +}; + +static void iTIFFDefaultDirectory(TIFF *tiff) +{ + /* Install the IM Tag field info */ + TIFFMergeFieldInfo(tiff, iTiffFieldInfo, TIFFArrayCount(iTiffFieldInfo)); +} + +void imFormatRegisterTIFF(void) +{ + TIFFSetTagExtender(iTIFFDefaultDirectory); + imFormatRegister(new imFormatTIFF()); +} + +int imFormatTIFF::Open(const char* file_name) +{ + this->tiff = TIFFOpen(file_name, "r"); + if (this->tiff == NULL) + return IM_ERR_FORMAT; + + // Return the compression of the first image in the file. + uint16 Compression = COMPRESSION_NONE; + TIFFGetField(this->tiff, TIFFTAG_COMPRESSION, &Compression); + int comp_index = iTIFFGetCompIndex(Compression); + if (comp_index == -1) return IM_ERR_COMPRESS; + strcpy(this->compression, iTIFFCompTable[comp_index]); + + this->image_count = TIFFNumberOfDirectories(this->tiff); + this->tile_buf = 0; + this->start_plane = 0; + + return IM_ERR_NONE; +} + +int imFormatTIFF::New(const char* file_name) +{ + this->tiff = TIFFOpen(file_name, "w"); + if (this->tiff == NULL) + return IM_ERR_OPEN; + + this->tile_buf = 0; + + return IM_ERR_NONE; +} + +void imFormatTIFF::Close() +{ + if (this->tile_buf) + { + for (int i = 0; i < this->tile_buf_count; i++) + free(this->tile_buf[i]); + free(this->tile_buf); + } + + TIFFClose(this->tiff); +} + +void* imFormatTIFF::Handle(int index) +{ + if (index == 0) + return (void*)this->tiff->tif_fd; + else if (index == 1) + return (void*)this->tiff; + else + return NULL; +} + +int imFormatTIFF::ReadImageInfo(int index) +{ + this->cpx_int = 0; + this->invert = 0; + this->lab_fix = 0; + this->extra_sample_size = 0; + + if (!TIFFSetDirectory(this->tiff, (tdir_t)index)) + return IM_ERR_ACCESS; + + imAttribTable* attrib_table = AttribTable(); + + uint16* attrib_start_plane = (uint16*)attrib_table->Get("MultiBandSelect"); + if (attrib_start_plane) + this->start_plane = *attrib_start_plane; + else + this->start_plane = 0; + + uint16* sub_ifd = (uint16*)attrib_table->Get("SubIFDSelect"); + + /* must clear the attribute list, because TIFF can have many different images */ + attrib_table->RemoveAll(); + + void* data = NULL; + if (TIFFGetField(this->tiff, TIFFTAG_DNGVERSION, &data) == 1 && data) + { + uint32 SubFileType = 0; + TIFFGetField(this->tiff, TIFFTAG_SUBFILETYPE, &SubFileType); + + uint16 SubIFDsCount = 0; + uint32* SubIFDs = NULL; + TIFFGetField(this->tiff, TIFFTAG_SUBIFD, &SubIFDsCount, &SubIFDs); + attrib_table->Set("SubIFDCount", IM_USHORT, 1, (void*)&SubIFDsCount); + + /* If is a DNG file, and has SubIFDs, + then ignore the thumbnail and position at the desired SubIFD. */ + + if (SubFileType == FILETYPE_REDUCEDIMAGE && SubIFDsCount != 0) + { + int index = sub_ifd? *sub_ifd: 0; + if (index >= SubIFDsCount) index = SubIFDsCount-1; + uint32 SubIFDOffset = SubIFDs[index]; + + /* Load the main image attributes, the SubIFD contains only a few attributes. */ + iTIFFReadAttributes(this->tiff, attrib_table); + + TIFFSetSubDirectory(this->tiff, SubIFDOffset); + } + } + + uint16 Compression = COMPRESSION_NONE; + TIFFGetField(this->tiff, TIFFTAG_COMPRESSION, &Compression); + int comp_index = iTIFFGetCompIndex(Compression); + if (comp_index == -1) return IM_ERR_COMPRESS; + strcpy(this->compression, iTIFFCompTable[comp_index]); + + if (Compression == COMPRESSION_JPEG || Compression == COMPRESSION_OJPEG) + TIFFSetField(this->tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + + uint32 Width; + if (!TIFFGetField(this->tiff, TIFFTAG_IMAGEWIDTH, &Width)) + return IM_ERR_FORMAT; + this->width = Width; + + uint32 Height; + if (!TIFFGetField(this->tiff, TIFFTAG_IMAGELENGTH, &Height)) + return IM_ERR_FORMAT; + this->height = Height; + + uint16 Photometric; + if (!TIFFGetField(this->tiff, TIFFTAG_PHOTOMETRIC, &Photometric)) + return IM_ERR_FORMAT; + attrib_table->Set("Photometric", IM_USHORT, 1, (void*)&Photometric); + + switch(Photometric) + { + case PHOTOMETRIC_MINISWHITE: + this->invert = 1; + case PHOTOMETRIC_LINEARRAW: + case PHOTOMETRIC_CFA: + case PHOTOMETRIC_LOGL: + case PHOTOMETRIC_MASK: + case PHOTOMETRIC_MINISBLACK: + this->file_color_mode = IM_GRAY; + break; + case PHOTOMETRIC_PALETTE: + this->file_color_mode = IM_MAP; + break; + case PHOTOMETRIC_RGB: + this->file_color_mode = IM_RGB; + break; + case PHOTOMETRIC_SEPARATED: + this->file_color_mode = IM_CMYK; + break; + case PHOTOMETRIC_YCBCR: + if (Compression == COMPRESSION_JPEG || Compression == COMPRESSION_OJPEG) + this->file_color_mode = IM_RGB; + else + this->file_color_mode = IM_YCBCR; + break; + case PHOTOMETRIC_CIELAB: + this->lab_fix = 1; + case PHOTOMETRIC_ITULAB: + case PHOTOMETRIC_ICCLAB: + this->file_color_mode = IM_LAB; + break; + case PHOTOMETRIC_LOGLUV: + this->file_color_mode = IM_XYZ; + break; + default: + return IM_ERR_DATA; + } + + if (Photometric == PHOTOMETRIC_LOGLUV || Photometric == PHOTOMETRIC_LOGL) + TIFFSetField(this->tiff, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT); + + uint16 SamplesPerPixel = 1, BitsPerSample = 1; + TIFFGetFieldDefaulted(this->tiff, TIFFTAG_BITSPERSAMPLE, &BitsPerSample); + TIFFGetFieldDefaulted(this->tiff, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + + if (BitsPerSample == 1 && this->file_color_mode == IM_GRAY) + this->file_color_mode = IM_BINARY; + + /* consistency checks */ + if (Photometric == PHOTOMETRIC_PALETTE && (SamplesPerPixel != 1 || BitsPerSample > 8)) + return IM_ERR_DATA; + + if (Photometric == PHOTOMETRIC_MASK && (SamplesPerPixel != 1 || BitsPerSample != 1)) + return IM_ERR_DATA; + + if ((Photometric == PHOTOMETRIC_CFA || Photometric == PHOTOMETRIC_LINEARRAW) && SamplesPerPixel == 3) /* when there are 3 sensors */ + this->file_color_mode = IM_RGB; + + if ((Photometric == PHOTOMETRIC_CFA || Photometric == PHOTOMETRIC_LINEARRAW) && BitsPerSample == 12) + this->convert_bpp = 12; + + uint16 PlanarConfig = PLANARCONFIG_CONTIG; + TIFFGetFieldDefaulted(this->tiff, TIFFTAG_PLANARCONFIG, &PlanarConfig); + + if (PlanarConfig == PLANARCONFIG_CONTIG && SamplesPerPixel > 1) + this->file_color_mode |= IM_PACKED; + + uint16 ExtraSamples = 0, *SampleInfo; + TIFFGetFieldDefaulted(this->tiff, TIFFTAG_EXTRASAMPLES, &ExtraSamples, &SampleInfo); + if (ExtraSamples == 1) + { + switch (SampleInfo[0]) + { + case EXTRASAMPLE_UNSPECIFIED: /* !unspecified data */ + case EXTRASAMPLE_ASSOCALPHA: /* data is pre-multiplied */ + case EXTRASAMPLE_UNASSALPHA: /* data is not pre-multiplied */ + this->file_color_mode |= IM_ALPHA; + break; + } + attrib_table->Set("ExtraSampleInfo", IM_USHORT, 1, (void*)&SampleInfo[0]); + } + else if ((ExtraSamples > 1) && (PlanarConfig == PLANARCONFIG_CONTIG)) + { + /* usually a multi band image, we read only one band */ + this->sample_size = (BitsPerSample*(SamplesPerPixel-ExtraSamples) + 7)/8; + this->extra_sample_size = (BitsPerSample*SamplesPerPixel + 7)/8; + + /* add space for the line buffer (this is more than necessary) */ + this->line_buffer_extra = TIFFScanlineSize(this->tiff); + } + + uint16 SampleFormat = SAMPLEFORMAT_UINT; + TIFFGetField(this->tiff, TIFFTAG_SAMPLEFORMAT, &SampleFormat); + switch(SampleFormat) + { + case SAMPLEFORMAT_VOID: + case SAMPLEFORMAT_UINT: + if (BitsPerSample < 8) + { + if (BitsPerSample != 1 && BitsPerSample != 2 && BitsPerSample != 4) + return IM_ERR_DATA; + + this->file_data_type = IM_BYTE; + this->convert_bpp = BitsPerSample; + } + else if (BitsPerSample == 8) + this->file_data_type = IM_BYTE; + else if (BitsPerSample <= 16) + this->file_data_type = IM_USHORT; + else if (BitsPerSample <= 32) + { + this->switch_type = 1; + this->file_data_type = IM_INT; + } + else + return IM_ERR_DATA; + break; + case SAMPLEFORMAT_INT: + if (BitsPerSample <= 8) + { + this->switch_type = 1; + this->file_data_type = IM_BYTE; + } + else if (BitsPerSample <= 16) + { + this->switch_type = 1; + this->file_data_type = IM_USHORT; + } + else if (BitsPerSample <= 32) + this->file_data_type = IM_INT; + else + return IM_ERR_DATA; + break; + case SAMPLEFORMAT_IEEEFP: + if (BitsPerSample == 32) + this->file_data_type = IM_FLOAT; + else if (BitsPerSample == 64) + { + this->switch_type = 1; + this->file_data_type = IM_FLOAT; + } + else + return IM_ERR_DATA; + break; + case SAMPLEFORMAT_COMPLEXINT: + if (BitsPerSample == 32) + { + this->cpx_int = 1; + this->file_data_type = IM_CFLOAT; // convert short to float + } + else if (BitsPerSample == 64) + { + this->cpx_int = 2; + this->file_data_type = IM_CFLOAT; // convert int to float + } + else + return IM_ERR_DATA; + break; + case SAMPLEFORMAT_COMPLEXIEEEFP: + if (BitsPerSample == 64) + this->file_data_type = IM_CFLOAT; + else if (BitsPerSample == 128) + { + this->switch_type = 1; + this->file_data_type = IM_CFLOAT; + } + else + return IM_ERR_DATA; + break; + default: + return IM_ERR_DATA; + } + + uint16 *rmap, *gmap, *bmap; + if (TIFFGetField(this->tiff, TIFFTAG_COLORMAP, &rmap, &gmap, &bmap)) + { + long palette[256]; + int palette_count = 1 << BitsPerSample; + + for (int c = 0; c < palette_count; c++) + { + palette[c] = imColorEncode((unsigned char)(rmap[c] >> 8), + (unsigned char)(gmap[c] >> 8), + (unsigned char)(bmap[c] >> 8)); + } + + imFileSetPalette(this, palette, palette_count); + } + + if (TIFFIsTiled(this->tiff)) + { + if (this->tile_buf) + { + for (int i = 0; i < this->tile_buf_count; i++) + free(this->tile_buf[i]); + free(this->tile_buf); + } + + uint32 tileWidth, tileLength; + TIFFGetField(this->tiff, TIFFTAG_TILEWIDTH, &tileWidth); + TIFFGetField(this->tiff, TIFFTAG_TILELENGTH, &tileLength); + this->tile_width = (int)tileWidth; + this->tile_height = (int)tileLength; + + this->tile_buf_count = (Width + tileWidth-1) / tileWidth; + if (PlanarConfig == PLANARCONFIG_SEPARATE) + this->tile_buf_count *= SamplesPerPixel; + this->tile_line_size = TIFFTileRowSize(this->tiff); + this->tile_line_raw_size = TIFFScanlineSize(this->tiff); + this->start_row = 0; + + this->tile_buf = (void**)malloc(sizeof(void*)*this->tile_buf_count); + int tile_size = TIFFTileSize(this->tiff); + for (int t = 0; t < this->tile_buf_count; t++) + this->tile_buf[t] = malloc(tile_size); + } + + if (SamplesPerPixel < imColorModeDepth(this->file_color_mode)) + return IM_ERR_DATA; + + if (SamplesPerPixel > 1 && imColorModeSpace(this->file_color_mode) == IM_GRAY) + { + /* multiband data, we read only one band */ + attrib_table->Set("MultiBandCount", IM_USHORT, 1, (void*)&SamplesPerPixel); + } + + uint16 Orientation; + TIFFGetFieldDefaulted(this->tiff, TIFFTAG_ORIENTATION, &Orientation); + switch (Orientation) + { + case ORIENTATION_TOPRIGHT: + case ORIENTATION_RIGHTTOP: + case ORIENTATION_LEFTTOP: + case ORIENTATION_TOPLEFT: + this->file_color_mode |= IM_TOPDOWN; + break; + } + attrib_table->Set("Orientation", IM_USHORT, 1, (void*)&Orientation); + + iTIFFReadAttributes(this->tiff, attrib_table); + + return IM_ERR_NONE; +} + +int imFormatTIFF::WriteImageInfo() +{ + this->file_color_mode = this->user_color_mode; + this->file_data_type = this->user_data_type; + this->lab_fix = 0; + + uint16 Compression = iTIFFCompCalc(this->compression, this->file_color_mode, this->file_data_type); + if (Compression == (uint16)-1) + return IM_ERR_COMPRESS; + + int comp_index = iTIFFGetCompIndex(Compression); + strcpy(this->compression, iTIFFCompTable[comp_index]); + + TIFFSetField(this->tiff, TIFFTAG_COMPRESSION, Compression); + + uint32 Width = this->width; + TIFFSetField(this->tiff, TIFFTAG_IMAGEWIDTH, Width); + + uint32 Height = this->height; + TIFFSetField(this->tiff, TIFFTAG_IMAGELENGTH, Height); + + static uint16 colorspace2photometric [] = + { + PHOTOMETRIC_RGB, + PHOTOMETRIC_PALETTE, + PHOTOMETRIC_MINISBLACK, + PHOTOMETRIC_MINISBLACK, + PHOTOMETRIC_SEPARATED, + PHOTOMETRIC_YCBCR, + PHOTOMETRIC_CIELAB, + (uint16)-1, // Pure Luv not supported + PHOTOMETRIC_LOGLUV // LogLuv Saved as XYZ + }; + + uint16 Photometric = colorspace2photometric[imColorModeSpace(this->file_color_mode)]; + + // Correction for sgi LogL + if (Compression == COMPRESSION_SGILOG && Photometric == PHOTOMETRIC_MINISBLACK) + Photometric = PHOTOMETRIC_LOGL; + + // Corrections for JPEG, automatic convert from RGB to YCbCr when writing + if (Compression == COMPRESSION_JPEG && Photometric == PHOTOMETRIC_RGB) + Photometric = PHOTOMETRIC_YCBCR; + + imAttribTable* attrib_table = AttribTable(); + + uint16* photometric = (uint16*)attrib_table->Get("Photometric"); + if (photometric) + { + if (*photometric == PHOTOMETRIC_MASK && Photometric == PHOTOMETRIC_MINISBLACK) + Photometric = PHOTOMETRIC_MASK; + else if (*photometric == PHOTOMETRIC_MINISWHITE && Photometric == PHOTOMETRIC_MINISBLACK) + Photometric = PHOTOMETRIC_MINISWHITE; + else if (*photometric == PHOTOMETRIC_ICCLAB && Photometric == PHOTOMETRIC_CIELAB) + Photometric = PHOTOMETRIC_ICCLAB; + else if (*photometric == PHOTOMETRIC_ITULAB && Photometric == PHOTOMETRIC_CIELAB) + Photometric = PHOTOMETRIC_ITULAB; + } + + if (Photometric == PHOTOMETRIC_CIELAB) + this->lab_fix = 1; + + TIFFSetField(this->tiff, TIFFTAG_PHOTOMETRIC, Photometric); + + // This is the default, and many software assume/handle only this, so we force it. + uint16 PlanarConfig = PLANARCONFIG_CONTIG; + TIFFSetField(this->tiff, TIFFTAG_PLANARCONFIG, PlanarConfig); + if (imColorModeDepth(this->file_color_mode) > 1) + this->file_color_mode |= IM_PACKED; + + // Corrections for JPEG, must be set after Photometric and PlanarConfig + if (Compression == COMPRESSION_JPEG && imColorModeSpace(this->file_color_mode) == IM_RGB) + TIFFSetField(this->tiff, TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB); + + // Compression options + int* zip_quality = (int*)attrib_table->Get("ZIPQuality"); + if (zip_quality && (Compression == COMPRESSION_DEFLATE || Compression == COMPRESSION_ADOBE_DEFLATE)) + TIFFSetField(this->tiff, TIFFTAG_ZIPQUALITY, *zip_quality); + + if (Compression == COMPRESSION_JPEG) + { + int* jpeg_quality = (int*)attrib_table->Get("JPEGQuality"); + if (jpeg_quality) + TIFFSetField(this->tiff, TIFFTAG_JPEGQUALITY, *jpeg_quality); + } + + // This is the default, and many software assume/handle only this, so we force it. + uint16 Orientation = ORIENTATION_TOPLEFT; + TIFFSetField(this->tiff, TIFFTAG_ORIENTATION, Orientation); + this->file_color_mode |= IM_TOPDOWN; + + static uint16 datatype2format[] = + { + SAMPLEFORMAT_UINT, + SAMPLEFORMAT_UINT, + SAMPLEFORMAT_INT, + SAMPLEFORMAT_IEEEFP, + SAMPLEFORMAT_COMPLEXIEEEFP + }; + uint16 SampleFormat = datatype2format[this->file_data_type]; + TIFFSetField(this->tiff, TIFFTAG_SAMPLEFORMAT, SampleFormat); + + uint16 BitsPerSample = (uint16)(imDataTypeSize(this->file_data_type)*8); + if (imColorModeSpace(this->file_color_mode) == IM_BINARY) + { + BitsPerSample = 1; + this->convert_bpp = 1; + } + TIFFSetField(this->tiff, TIFFTAG_BITSPERSAMPLE, BitsPerSample); + + // Correction for Luv, this will change BitsperSample and SampleFormat + if (Photometric == PHOTOMETRIC_LOGLUV || Photometric == PHOTOMETRIC_LOGL) + TIFFSetField(this->tiff, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT); + + uint16 SamplesPerPixel = (uint16)imColorModeDepth(this->file_color_mode); + TIFFSetField(this->tiff, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel); + + if (imColorModeHasAlpha(this->file_color_mode)) + { + uint16 ExtraSamples = 1, SampleInfo[1] = {EXTRASAMPLE_UNASSALPHA}; + uint16* sample_info = (uint16*)attrib_table->Get("ExtraSampleInfo"); + if (sample_info) SampleInfo[0] = *sample_info; + TIFFSetField(this->tiff, TIFFTAG_EXTRASAMPLES, ExtraSamples, SampleInfo); + } + + if (imColorModeSpace(this->file_color_mode) == IM_MAP) + { + uint16 rmap[256], gmap[256], bmap[256]; + memset(rmap, 0, 256 * 2); + memset(gmap, 0, 256 * 2); + memset(bmap, 0, 256 * 2); + + unsigned char r, g, b; + for (int c = 0; c < this->palette_count; c++) + { + imColorDecode(&r, &g, &b, this->palette[c]); + rmap[c] = (uint16)(((uint16)r) << 8); + gmap[c] = (uint16)(((uint16)g) << 8); + bmap[c] = (uint16)(((uint16)b) << 8); + } + + TIFFSetField(this->tiff, TIFFTAG_COLORMAP, rmap, gmap, bmap); + } + + // Force libTIFF to calculate best RowsPerStrip + uint32 RowsPerStrip = (uint32)-1; + RowsPerStrip = TIFFDefaultStripSize(this->tiff, RowsPerStrip); + TIFFSetField(this->tiff, TIFFTAG_ROWSPERSTRIP, RowsPerStrip); + + iTIFFWriteAttributes(this->tiff, attrib_table); + + return IM_ERR_NONE; +} + +static void iTIFFExpandComplexInt(void* line_buffer, int count, int cpx_int) +{ + count *= 2; + + // conversion will be done in place + + if (cpx_int == 1) + { + // convert short to float, expanding from 16 to 32 bits + short* short_buffer = (short*)line_buffer; + float* float_buffer = (float*)line_buffer; + + float_buffer += count-1; // from end to start + short_buffer += count-1; + + for (int i = 0; i < count; i++) + *float_buffer-- = (float)(*short_buffer--); + } + else + { + // convert int to float, same size not expanding + int* int_buffer = (int*)line_buffer; + float* float_buffer = (float*)line_buffer; + + for (int i = 0; i < count; i++) + *float_buffer++ = (float)(*int_buffer++); + } +} + +static void iTIFFExtraSamplesFix(unsigned char* line_buffer, int width, int sample_size, int extra_sample_size, int plane) +{ + /* ignore all the other extra samples, here the samples are packed */ + for (int i = 1; i < width; i++) + { + memcpy(line_buffer + i*sample_size, line_buffer + i*extra_sample_size + plane, sample_size); + } +} + +/* +For CIELab (PhotometricInterpretation = 8), the L* component is encoded in 8 bits as an unsigned integer +range [0,255], and encoded in 16 bits as an unsigned integer range [0,65535]. The a* and b* components +are encoded in 8 bits as signed integers range [-128,127], and encoded in 16 bits as signed integers range [- +32768,32767]. The 8 bit chrominance values are exactly equal to the 1976 CIE a* and b* values, while the +16 bit values are equal to 256 times the 1976 CIE a* and b* values. + +For ICCLab (PhotometricInterpretation = 9), the L* component is encoded in 8 bits as an unsigned integer +range [0,255], and encoded in 16 bits as an unsigned integer range [0,65280]. The a* and b* components +are encoded in 8 bits as unsigned integers range [0,255], and encoded in 16 bits as unsigned integers range +[0,65535]. The 8 bit chrominance values are exactly equal to the 1976 CIE a* and b* values plus 128, +while the 16 bit values are equal to 256 times the 1976 CIE a* and b* values plus 32768 (this is also 256 +times the 8 bit encoding). PhotometricInterpretation 9 is designed to match the encoding used by the ICC +profile specification. +*/ + +static void iTIFFLabFix(void* line_buffer, int width, int data_type, int is_new) +{ + if (data_type == IM_BYTE) + { + imbyte* byte_buffer = (imbyte*)line_buffer; + + int offset = 128; + if (is_new) offset = -128; + + for (int i = 0; i < width; i++) + { + *(byte_buffer+1) = (imbyte)(*((char*)byte_buffer+1) + offset); + *(byte_buffer+2) = (imbyte)(*((char*)byte_buffer+2) + offset); + + byte_buffer += 3; + } + } + else if (data_type == IM_USHORT) + { + imushort* ushort_buffer = (imushort*)line_buffer; + + int offset = 32768; + if (is_new) offset = -32768; + + for (int i = 0; i < width; i++) + { + *(ushort_buffer+1) = (imushort)(*((short*)ushort_buffer+1) + offset); + *(ushort_buffer+2) = (imushort)(*((short*)ushort_buffer+2) + offset); + + ushort_buffer += 3; + } + } + // Do NOT know how it is encoded for other data types. +} + +int imFormatTIFF::ReadTileline(void* line_buffer, int row, int plane) +{ + int t; + + if (row == 0) + this->start_row = 0; + + if (row == this->start_row + this->tile_width) + this->start_row = row; + + // load a line of tiles + if (row == this->start_row) + { + int x = 0; + for (t = 0; t < this->tile_buf_count; t++) + { + if (TIFFReadTile(this->tiff, this->tile_buf[t], x, start_row, 0, (tsample_t)plane) <= 0) + return -1; + + x += this->tile_width; + } + } + + int line_size = this->tile_line_size; + int tile_line = row - this->start_row; + + for (t = 0; t < this->tile_buf_count; t++) + { + if (t == this->tile_buf_count-1) + { + int extra = this->tile_line_size*this->tile_buf_count - this->tile_line_raw_size; + line_size -= extra; + } + + memcpy(line_buffer, (imbyte*)(this->tile_buf[t]) + tile_line*tile_line_size, line_size); + line_buffer = (imbyte*)(line_buffer) + line_size; + } + + return 1; +} + +int imFormatTIFF::ReadImageData(void* data) +{ + int count = imFileLineBufferCount(this); + + imCounterTotal(this->counter, count, "Reading TIFF..."); + + int row = 0, plane = this->start_plane; + for (int i = 0; i < count; i++) + { + if (TIFFIsTiled(this->tiff)) + { + if (ReadTileline(this->line_buffer, row, (tsample_t)plane) <= 0) + return IM_ERR_ACCESS; + } + else + { + if (TIFFReadScanline(this->tiff, this->line_buffer, row, (tsample_t)plane) <= 0) + return IM_ERR_ACCESS; + } + + if (this->invert && this->file_data_type == IM_BYTE) + { + unsigned char* buf = (unsigned char*)this->line_buffer; + for (int b = 0; b < this->line_buffer_size; b++) + { + *buf = ~(*buf); + buf++; + } + } + + if (this->cpx_int) + { + int line_count = imImageLineCount(this->width, this->user_color_mode); + iTIFFExpandComplexInt(this->line_buffer, line_count, this->cpx_int); + } + + if (this->lab_fix) + iTIFFLabFix(this->line_buffer, this->width, this->file_data_type, 0); + + if (this->extra_sample_size) + iTIFFExtraSamplesFix((imbyte*)this->line_buffer, this->width, this->sample_size, this->extra_sample_size, plane); + + imFileLineBufferRead(this, data, row, plane); + + if (!imCounterInc(this->counter)) + return IM_ERR_COUNTER; + + imFileLineBufferInc(this, &row, &plane); + } + + return IM_ERR_NONE; +} + +int imFormatTIFF::WriteImageData(void* data) +{ + int count = imFileLineBufferCount(this); + + imCounterTotal(this->counter, count, "Writing TIFF..."); + + int row = 0, plane = 0; + for (int i = 0; i < count; i++) + { + imFileLineBufferWrite(this, data, row, plane); + + if (this->lab_fix) + iTIFFLabFix(this->line_buffer, this->width, this->file_data_type, 1); + + if (TIFFWriteScanline(this->tiff, this->line_buffer, row, (tsample_t)plane) <= 0) + return IM_ERR_ACCESS; + + if (!imCounterInc(this->counter)) + return IM_ERR_COUNTER; + + imFileLineBufferInc(this, &row, &plane); + } + + this->image_count++; + + if (!TIFFWriteDirectory(this->tiff)) + return IM_ERR_ACCESS; + + return IM_ERR_NONE; +} + +int imFormatTIFF::CanWrite(const char* compression, int color_mode, int data_type) const +{ + if (!compression) + return IM_ERR_NONE; + + if (imColorModeSpace(color_mode) == IM_LUV) + return IM_ERR_DATA; + + uint16 Compression = iTIFFCompCalc(compression, color_mode, data_type); + if (Compression == (uint16)-1) + return IM_ERR_COMPRESS; + + /* no support for 2 bpp or 4 bpp */ + if (Compression == COMPRESSION_THUNDERSCAN || Compression == COMPRESSION_NEXT) + return IM_ERR_COMPRESS; + + /* Binary compression restrictions */ + if ((Compression == COMPRESSION_CCITTRLE || Compression == COMPRESSION_CCITTRLEW || + Compression == COMPRESSION_CCITTFAX3 || Compression == COMPRESSION_CCITTFAX4) && + imColorModeSpace(color_mode) != IM_BINARY) + return IM_ERR_COMPRESS; + + /* JPEG compression restrictions */ + if (Compression == COMPRESSION_JPEG && + (data_type != IM_BYTE || + imColorModeSpace(color_mode) == IM_MAP || imColorModeSpace(color_mode) == IM_BINARY)) + return IM_ERR_COMPRESS; + + /* Pixar log accepts only 3 types */ + if (Compression == COMPRESSION_PIXARLOG && + data_type != IM_BYTE && data_type != IM_USHORT && data_type != IM_FLOAT) + return IM_ERR_COMPRESS; + + /* SGI Luv compression restrictions */ + if ((Compression == COMPRESSION_SGILOG || Compression == COMPRESSION_SGILOG24) && + (imColorModeSpace(color_mode) != IM_XYZ || data_type != IM_FLOAT)) + return IM_ERR_COMPRESS; + + return IM_ERR_NONE; +} -- cgit v1.2.3