// ************************************************************************** // * TIFFCleanup() * // ************************************************************************** // Auxiliary function to free the TIFF structure. Given structure will be // completetly freed, so you should save opened file handle and pointer // to the close procedure in external variables before calling // _TIFFCleanup(), if you will need these ones to close the file. // // @param tif A TIFF pointer. public static void TIFFCleanup(TIFF tif) { if (tif.tif_mode != O.RDONLY) { TIFFFlush(tif); // Flush buffered data and directory (if dirty). } tif.tif_cleanup(tif); TIFFFreeDirectory(tif); tif.tif_dirlist.Clear(); // Clean up client info links tif.tif_clientinfo.Clear(); tif.tif_rawdata = null; tif.tif_fieldinfo.Clear(); }
// Read the next TIFF directory from a file // and convert it to the internal format. // We read directories sequentially. public static bool TIFFReadDirectory(TIFF tif) { string module="TIFFReadDirectory"; ushort iv; uint v; TIFFFieldInfo fip; bool diroutoforderwarning=false; bool haveunknowntags=false; tif.tif_diroff=tif.tif_nextdiroff; // Check whether we have the last offset or bad offset (IFD looping). if(!TIFFCheckDirOffset(tif, tif.tif_nextdiroff)) return false; // Cleanup any previous compression state. tif.tif_cleanup(tif); tif.tif_curdir++; List<TIFFDirEntry> dir=null; ushort dircount=TIFFFetchDirectory(tif, tif.tif_nextdiroff, ref dir, ref tif.tif_nextdiroff); if(dircount==0) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Failed to read directory at offset{1}", tif.tif_name, tif.tif_nextdiroff); return false; } tif.tif_flags&=~TIF_FLAGS.TIFF_BEENWRITING; // reset before new dir // Setup default value and then make a pass over // the fields to check type and tag information, // and to extract info required to size data // structures. A second pass is made afterwards // to read in everthing not taken in the first pass. TIFFDirectory td=tif.tif_dir; // free any old stuff and reinit TIFFFreeDirectory(tif); TIFFDefaultDirectory(tif); // Electronic Arts writes gray-scale TIFF files // without a PlanarConfiguration directory entry. // Thus we setup a default value here, even though // the TIFF spec says there is no default value. TIFFSetField(tif, TIFFTAG.PLANARCONFIG, PLANARCONFIG.CONTIG); // Sigh, we must make a separate pass through the // directory for the following reason: // // We must process the Compression tag in the first pass // in order to merge in codec-private tag definitions (otherwise // we may get complaints about unknown tags). However, the // Compression tag may be dependent on the SamplesPerPixel // tag value because older TIFF specs permited Compression // to be written as a SamplesPerPixel-count tag entry. // Thus if we don't first figure out the correct SamplesPerPixel // tag value then we may end up ignoring the Compression tag // value because it has an incorrect count value (if the // true value of SamplesPerPixel is not 1). // // It sure would have been nice if Aldus had really thought // this stuff through carefully. foreach(TIFFDirEntry dp in dir) { if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { TIFFSwab(ref dp.tdir_tag); TIFFSwab(ref dp.tdir_type); TIFFSwab(ref dp.tdir_count); TIFFSwab(ref dp.tdir_offset); } if((TIFFTAG)dp.tdir_tag==TIFFTAG.SAMPLESPERPIXEL) { if(!TIFFFetchNormalTag(tif, dp)) return false; dp.tdir_tag=IGNORE; } } // First real pass over the directory. int fix=0; foreach(TIFFDirEntry dp in dir) { if(dp.tdir_tag==IGNORE) continue; if(fix>=tif.tif_fieldinfo.Count) fix=0; // Silicon Beach (at least) writes unordered // directory tags (violating the spec). Handle // it here, but be obnoxious (maybe they'll fix it?). if(dp.tdir_tag<(ushort)tif.tif_fieldinfo[fix].field_tag) { if(!diroutoforderwarning) { TIFFWarningExt(tif.tif_clientdata, module, "{0}: invalid TIFF directory; tags are not sorted in ascending order", tif.tif_name); diroutoforderwarning=true; } fix=0; // O(n^2) } while(fix<tif.tif_fieldinfo.Count&&(ushort)tif.tif_fieldinfo[fix].field_tag<dp.tdir_tag) fix++; if(fix>=tif.tif_fieldinfo.Count||(ushort)tif.tif_fieldinfo[fix].field_tag!=dp.tdir_tag) { // Unknown tag ... we'll deal with it below haveunknowntags=true; continue; } // Null out old tags that we ignore. if(tif.tif_fieldinfo[fix].field_bit==FIELD.IGNORE) { dp.tdir_tag=IGNORE; continue; } // Check data type. fip=tif.tif_fieldinfo[fix]; bool docontinue=false; while(dp.tdir_type!=(ushort)fip.field_type&&fix<tif.tif_fieldinfo.Count) { if(fip.field_type==TIFFDataType.TIFF_ANY) break; // wildcard fip=tif.tif_fieldinfo[++fix]; if(fix>=tif.tif_fieldinfo.Count||(ushort)fip.field_tag!=dp.tdir_tag) { TIFFWarningExt(tif.tif_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", tif.tif_name, dp.tdir_type, tif.tif_fieldinfo[fix-1].field_name); dp.tdir_tag=IGNORE; docontinue=true; break; } } if(docontinue) continue; // Check count if known in advance. if(fip.field_readcount!=TIFF_VARIABLE) { uint expected=(fip.field_readcount==TIFF_SPP)?td.td_samplesperpixel:(uint)fip.field_readcount; if(!CheckDirCount(tif, dp, expected)) { dp.tdir_tag=IGNORE; continue; } } switch((TIFFTAG)dp.tdir_tag) { case TIFFTAG.COMPRESSION: // The 5.0 spec says the Compression tag has // one value, while earlier specs say it has // one value per sample. Because of this, we // accept the tag if one value is supplied. if(dp.tdir_count==1) { //was v = TIFFExtractData(tif, dp.tdir_type, dp.tdir_offset); v=(tif.tif_header.tiff_magic==TIFF_BIGENDIAN?(dp.tdir_offset>>tif_typeshift[dp.tdir_type])&tif_typemask[dp.tdir_type]:dp.tdir_offset&tif_typemask[dp.tdir_type]); if(!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (ushort)v)) return false; break; // XXX: workaround for broken TIFFs } if((TIFFDataType)dp.tdir_type==TIFFDataType.TIFF_LONG) { if(!TIFFFetchPerSampleLongs(tif, dp, out v)||!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (ushort)v)) return false; } else { if(!TIFFFetchPerSampleShorts(tif, dp, out iv)||!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, iv)) return false; } dp.tdir_tag=IGNORE; break; case TIFFTAG.STRIPOFFSETS: case TIFFTAG.STRIPBYTECOUNTS: case TIFFTAG.TILEOFFSETS: case TIFFTAG.TILEBYTECOUNTS: TIFFSetFieldBit(tif, fip.field_bit); break; case TIFFTAG.IMAGEWIDTH: case TIFFTAG.IMAGELENGTH: case TIFFTAG.IMAGEDEPTH: case TIFFTAG.TILELENGTH: case TIFFTAG.TILEWIDTH: case TIFFTAG.TILEDEPTH: case TIFFTAG.PLANARCONFIG: case TIFFTAG.ROWSPERSTRIP: case TIFFTAG.EXTRASAMPLES: if(!TIFFFetchNormalTag(tif, dp)) return false; dp.tdir_tag=IGNORE; break; } } // If we saw any unknown tags, make an extra pass over the directory // to deal with them. This must be done separately because the tags // could have become known when we registered a codec after finding // the Compression tag. In a correctly-sorted directory there's // no problem because Compression will come before any codec-private // tags, but if the sorting is wrong that might not hold. if(haveunknowntags) { fix=0; foreach(TIFFDirEntry dp in dir) { if(dp.tdir_tag==IGNORE) continue; if(fix>=tif.tif_fieldinfo.Count||dp.tdir_tag<(ushort)tif.tif_fieldinfo[fix].field_tag) fix=0; // O(n^2) while(fix<tif.tif_fieldinfo.Count&&(ushort)tif.tif_fieldinfo[fix].field_tag<dp.tdir_tag) fix++; if(fix>=tif.tif_fieldinfo.Count||(ushort)tif.tif_fieldinfo[fix].field_tag!=dp.tdir_tag) { TIFFWarningExt(tif.tif_clientdata, module, "{0}: unknown field with tag %d (0x{1:X}) encountered", tif.tif_name, dp.tdir_tag, dp.tdir_tag); if(!_TIFFMergeFieldInfo(tif, TIFFCreateAnonFieldInfo(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type))) { TIFFWarningExt(tif.tif_clientdata, module, "Registering anonymous field with tag {0} (0x{1:X}) failed", dp.tdir_tag, dp.tdir_tag); dp.tdir_tag=IGNORE; continue; } fix=0; while(fix<tif.tif_fieldinfo.Count&&(ushort)tif.tif_fieldinfo[fix].field_tag<dp.tdir_tag) fix++; } // Check data type. fip=tif.tif_fieldinfo[fix]; while(dp.tdir_type!=(ushort)fip.field_type&&fix<tif.tif_fieldinfo.Count) { if(fip.field_type==TIFFDataType.TIFF_ANY) // wildcard break; fip=tif.tif_fieldinfo[++fix]; if(fix>=tif.tif_fieldinfo.Count||(ushort)fip.field_tag!=dp.tdir_tag) { TIFFWarningExt(tif.tif_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", tif.tif_name, dp.tdir_type, tif.tif_fieldinfo[fix-1].field_name); dp.tdir_tag=IGNORE; break; } } } } // Allocate directory structure and setup defaults. if(!TIFFFieldSet(tif, FIELD.IMAGEDIMENSIONS)) { MissingRequired(tif, "ImageLength"); return false; } // Setup appropriate structures (by strip or by tile) if(!TIFFFieldSet(tif, FIELD.TILEDIMENSIONS)) { td.td_nstrips=(uint)TIFFNumberOfStrips(tif); td.td_tilewidth=td.td_imagewidth; td.td_tilelength=td.td_rowsperstrip; td.td_tiledepth=td.td_imagedepth; tif.tif_flags&=~TIF_FLAGS.TIFF_ISTILED; } else { td.td_nstrips=(uint)TIFFNumberOfTiles(tif); tif.tif_flags|=TIF_FLAGS.TIFF_ISTILED; } if(td.td_nstrips==0) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: cannot handle zero number of {1}", tif.tif_name, isTiled(tif)?"tiles":"strips"); return false; } td.td_stripsperimage=td.td_nstrips; if(td.td_planarconfig==PLANARCONFIG.SEPARATE) td.td_stripsperimage/=td.td_samplesperpixel; if(!TIFFFieldSet(tif, FIELD.STRIPOFFSETS)) { MissingRequired(tif, isTiled(tif)?"TileOffsets":"StripOffsets"); return false; } // Second pass: extract other information. foreach(TIFFDirEntry dp in dir) { if(dp.tdir_tag==IGNORE) continue; switch((TIFFTAG)dp.tdir_tag) { case TIFFTAG.MINSAMPLEVALUE: case TIFFTAG.MAXSAMPLEVALUE: case TIFFTAG.BITSPERSAMPLE: case TIFFTAG.DATATYPE: case TIFFTAG.SAMPLEFORMAT: // The 5.0 spec says the Compression tag has // one value, while earlier specs say it has // one value per sample. Because of this, we // accept the tag if one value is supplied. // // The MinSampleValue, MaxSampleValue, BitsPerSample // DataType and SampleFormat tags are supposed to be // written as one value/sample, but some vendors // incorrectly write one value only -- so we accept // that as well (yech). Other vendors write correct // value for NumberOfSamples, but incorrect one for // BitsPerSample and friends, and we will read this // too. if(dp.tdir_count==1) { //was v = TIFFExtractData(tif, dp.tdir_type, dp.tdir_offset); v=(tif.tif_header.tiff_magic==TIFF_BIGENDIAN?(dp.tdir_offset>>tif_typeshift[dp.tdir_type])&tif_typemask[dp.tdir_type]:dp.tdir_offset&tif_typemask[dp.tdir_type]); if(!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (ushort)v)) return false; // XXX: workaround for broken TIFFs } else if((TIFFTAG)dp.tdir_tag==TIFFTAG.BITSPERSAMPLE&&(TIFFDataType)dp.tdir_type==TIFFDataType.TIFF_LONG) { if(!TIFFFetchPerSampleLongs(tif, dp, out v)||!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (ushort)v)) return false; } else { if(!TIFFFetchPerSampleShorts(tif, dp, out iv)||!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, iv)) return false; } break; case TIFFTAG.SMINSAMPLEVALUE: case TIFFTAG.SMAXSAMPLEVALUE: { double dv=0.0; if(!TIFFFetchPerSampleAnys(tif, dp, out dv)||!TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, dv)) return false; } break; case TIFFTAG.STRIPOFFSETS: case TIFFTAG.TILEOFFSETS: if(!TIFFFetchStripThing(tif, dp, td.td_nstrips, ref td.td_stripoffset)) return false; break; case TIFFTAG.STRIPBYTECOUNTS: case TIFFTAG.TILEBYTECOUNTS: if(!TIFFFetchStripThing(tif, dp, td.td_nstrips, ref td.td_stripbytecount)) return false; break; case TIFFTAG.COLORMAP: case TIFFTAG.TRANSFERFUNCTION: { // TransferFunction can have either 1x or 3x // data values; Colormap can have only 3x // items. v=1u<<td.td_bitspersample; if((TIFFTAG)dp.tdir_tag==TIFFTAG.COLORMAP||dp.tdir_count!=v) { if(!CheckDirCount(tif, dp, 3*v)) break; } //v*=sizeof(ushort); ushort[] cp=null; try { cp=new ushort[dp.tdir_count]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to read \"TransferFunction\" tag"); } if(cp==null) break; if(TIFFFetchData(tif, dp, cp)==0) break; // This deals with there being // only one array to apply to // all samples. uint c=1u<<td.td_bitspersample; if(dp.tdir_count==c) TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, cp, cp, cp); else { try { ushort[] cp1=new ushort[v]; ushort[] cp2=new ushort[v]; ushort[] cp3=new ushort[v]; Array.Copy(cp, 0*v, cp1, 0, v); Array.Copy(cp, 1*v, cp2, 0, v); Array.Copy(cp, 2*v, cp3, 0, v); TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, cp1, cp2, cp3); } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to read \"TransferFunction\" tag"); } } } break; case TIFFTAG.PAGENUMBER: case TIFFTAG.HALFTONEHINTS: case TIFFTAG.YCBCRSUBSAMPLING: case TIFFTAG.DOTRANGE: TIFFFetchShortPair(tif, dp); break; case TIFFTAG.REFERENCEBLACKWHITE: TIFFFetchRefBlackWhite(tif, dp); break; // BEGIN REV 4.0 COMPATIBILITY case TIFFTAG.OSUBFILETYPE: v=0; //was switch(TIFFExtractData(tif, dp.tdir_type, dp.tdir_offset)) switch((OFILETYPE)(tif.tif_header.tiff_magic==TIFF_BIGENDIAN?(dp.tdir_offset>>tif_typeshift[dp.tdir_type])&tif_typemask[dp.tdir_type]:dp.tdir_offset&tif_typemask[dp.tdir_type])) { case OFILETYPE.REDUCEDIMAGE: v=(uint)FILETYPE.REDUCEDIMAGE; break; case OFILETYPE.PAGE: v=(uint)FILETYPE.PAGE; break; } if(v!=0) TIFFSetField(tif, TIFFTAG.SUBFILETYPE, v); break; // END REV 4.0 COMPATIBILITY default: TIFFFetchNormalTag(tif, dp); break; } } // Verify Palette image has a Colormap. if(td.td_photometric==PHOTOMETRIC.PALETTE&&!TIFFFieldSet(tif, FIELD.COLORMAP)) { MissingRequired(tif, "Colormap"); return false; } // Attempt to deal with a missing StripByteCounts tag. if(!TIFFFieldSet(tif, FIELD.STRIPBYTECOUNTS)) { // Some manufacturers violate the spec by not giving // the size of the strips. In this case, assume there // is one uncompressed strip of data. if((td.td_planarconfig==PLANARCONFIG.CONTIG&&td.td_nstrips>1)|| (td.td_planarconfig==PLANARCONFIG.SEPARATE&&td.td_nstrips!=td.td_samplesperpixel)) { MissingRequired(tif, "StripByteCounts"); return false; } TIFFWarningExt(tif.tif_clientdata, module, "{0}: TIFF directory is missing required \"{1}\" field, calculating from imagelength", tif.tif_name, TIFFFieldWithTag(tif, TIFFTAG.STRIPBYTECOUNTS).field_name); if(EstimateStripByteCounts(tif, dir)<0) return false; // Assume we have wrong StripByteCount value (in case of single strip) in // following cases: // - it is equal to zero along with StripOffset; // - it is larger than file itself (in case of uncompressed image); // - it is smaller than the size of the bytes per row multiplied on the // number of rows. The last case should not be checked in the case of // writing new image, because we may do not know the exact strip size // until the whole image will be written and directory dumped out. } else if(td.td_nstrips==1&&td.td_stripoffset[0]!=0&&((td.td_stripbytecount[0]==0&&td.td_stripoffset[0]!=0)|| (td.td_compression==COMPRESSION.NONE&&td.td_stripbytecount[0]>TIFFGetFileSize(tif)-td.td_stripoffset[0])|| (tif.tif_mode==O.RDONLY&&td.td_compression==COMPRESSION.NONE&&td.td_stripbytecount[0]<TIFFScanlineSize(tif)*td.td_imagelength))) { // XXX: Plexus (and others) sometimes give a value of zero for // a tag when they don't know what the correct value is! Try // and handle the simple case of estimating the size of a one // strip image. TIFFWarningExt(tif.tif_clientdata, module, "{0}: Bogus \"{1}\" field, ignoring and calculating from imagelength", tif.tif_name, TIFFFieldWithTag(tif, TIFFTAG.STRIPBYTECOUNTS).field_name); if(EstimateStripByteCounts(tif, dir)<0) return false; } else if(td.td_planarconfig==PLANARCONFIG.CONTIG&&td.td_nstrips>2&&td.td_compression==COMPRESSION.NONE&&td.td_stripbytecount[0]!=td.td_stripbytecount[1]&&td.td_stripbytecount[0]!=0&&td.td_stripbytecount[1]!=0) { // XXX: Some vendors fill StripByteCount array with absolutely // wrong values (it can be equal to StripOffset array, for // example). Catch this case here. TIFFWarningExt(tif.tif_clientdata, module, "{0}: Wrong \"{1}\" field, ignoring and calculating from imagelength", tif.tif_name, TIFFFieldWithTag(tif, TIFFTAG.STRIPBYTECOUNTS).field_name); if(EstimateStripByteCounts(tif, dir)<0) return false; } dir=null; if(!TIFFFieldSet(tif, FIELD.MAXSAMPLEVALUE)) td.td_maxsamplevalue=(ushort)((1<<td.td_bitspersample)-1); // Setup default compression scheme. // XXX: We can optimize checking for the strip bounds using the sorted // bytecounts array. See also comments for TIFFAppendToStrip() // function in tif_write.cs. if(td.td_nstrips>1) { td.td_stripbytecountsorted=1; for(uint strip=1; strip<td.td_nstrips; strip++) { if(td.td_stripoffset[strip-1]>td.td_stripoffset[strip]) { td.td_stripbytecountsorted=0; break; } } } if(!TIFFFieldSet(tif, FIELD.COMPRESSION)) TIFFSetField(tif, TIFFTAG.COMPRESSION, COMPRESSION.NONE); // Some manufacturers make life difficult by writing // large amounts of uncompressed data as a single strip. // This is contrary to the recommendations of the spec. // The following makes an attempt at breaking such images // into strips closer to the recommended 8k bytes. A // side effect, however, is that the RowsPerStrip tag // value may be changed. if(td.td_nstrips==1&&td.td_compression==COMPRESSION.NONE&&(tif.tif_flags&(TIF_FLAGS.TIFF_STRIPCHOP|TIF_FLAGS.TIFF_ISTILED))==TIF_FLAGS.TIFF_STRIPCHOP) ChopUpSingleUncompressedStrip(tif); // Reinitialize i/o since we are starting on a new directory. tif.tif_row=0xffffffff; tif.tif_curstrip=0xffffffff; tif.tif_col=0xffffffff; tif.tif_curtile=0xffffffff; tif.tif_tilesize=-1; tif.tif_scanlinesize=(uint)TIFFScanlineSize(tif); if(tif.tif_scanlinesize==0) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: cannot handle zero scanline size", tif.tif_name); return false; } if(isTiled(tif)) { tif.tif_tilesize=TIFFTileSize(tif); if(tif.tif_tilesize==0) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: cannot handle zero tile size", tif.tif_name); return false; } } else { if(TIFFStripSize(tif)==0) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: cannot handle zero strip size", tif.tif_name); return false; } } return true; }
// Unlink the specified directory from the directory chain. public static bool TIFFUnlinkDirectory(TIFF tif, ushort dirn) { string module="TIFFUnlinkDirectory"; if(tif.tif_mode==O.RDONLY) { TIFFErrorExt(tif.tif_clientdata, module, "Can not unlink directory in read-only file"); return false; } // Go to the directory before the one we want // to unlink and nab the offset of the link // field we'll need to patch. uint nextdir=tif.tif_header.tiff_diroff; uint off=4; uint n; for(n=dirn-1u; n>0; n--) { if(nextdir==0) { TIFFErrorExt(tif.tif_clientdata, module, "Directory {0} does not exist", dirn); return false; } if(!TIFFAdvanceDirectory(tif, ref nextdir, out off)) return false; } // Advance to the directory to be unlinked and fetch // the offset of the directory that follows. uint @null; if(!TIFFAdvanceDirectory(tif, ref nextdir, out @null)) return false; // Go back and patch the link field of the preceding // directory to point to the offset of the directory // that follows. TIFFSeekFile(tif, off, SEEK.SET); if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) TIFFSwab(ref nextdir); if(!WriteOK(tif, nextdir)) { TIFFErrorExt(tif.tif_clientdata, module, "Error writing directory link"); return false; } // Leave directory state setup safely. We don't have // facilities for doing inserting and removing directories, // so it's safest to just invalidate everything. This // means that the caller can only append to the directory // chain. tif.tif_cleanup(tif); if((tif.tif_flags&TIF_FLAGS.TIFF_MYBUFFER)!=0&&tif.tif_rawdata!=null) { tif.tif_rawdata=null; tif.tif_rawcc=0; } tif.tif_flags&=~(TIF_FLAGS.TIFF_BEENWRITING|TIF_FLAGS.TIFF_BUFFERSETUP|TIF_FLAGS.TIFF_POSTENCODE); TIFFFreeDirectory(tif); TIFFDefaultDirectory(tif); tif.tif_diroff=0; // force link on next write tif.tif_nextdiroff=0; // next write must be at end tif.tif_curoff=0; tif.tif_row=0xffffffff; tif.tif_curstrip=0xffffffff; return true; }
static bool _TIFFVSetField(TIFF tif, TIFFTAG tag, TIFFDataType dt, object[] ap) { string module="_TIFFVSetField"; TIFFDirectory td=tif.tif_dir; bool status=true; uint v; switch(tag) { case TIFFTAG.SUBFILETYPE: td.td_subfiletype=(FILETYPE)__GetAsUint(ap, 0); break; case TIFFTAG.IMAGEWIDTH: td.td_imagewidth=__GetAsUint(ap, 0); break; case TIFFTAG.IMAGELENGTH: td.td_imagelength=__GetAsUint(ap, 0); break; case TIFFTAG.BITSPERSAMPLE: td.td_bitspersample=__GetAsUshort(ap, 0); // If the data require post-decoding processing to byte-swap // samples, set it up here. Note that since tags are required // to be ordered, compression code can override this behaviour // in the setup method if it wants to roll the post decoding // work in with its normal work. if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { if(td.td_bitspersample==16) tif.tif_postdecode=TIFFSwab16BitData; else if(td.td_bitspersample==24) tif.tif_postdecode=TIFFSwab24BitData; else if(td.td_bitspersample==32) tif.tif_postdecode=TIFFSwab32BitData; else if(td.td_bitspersample==64) tif.tif_postdecode=TIFFSwab64BitData; else if(td.td_bitspersample==128) tif.tif_postdecode=TIFFSwab64BitData; // two 64's } break; case TIFFTAG.COMPRESSION: v=__GetAsUint(ap, 0)&0xffff; // If we're changing the compression scheme, the notify the // previous module so that it can cleanup any state it's setup. if(TIFFFieldSet(tif, FIELD.COMPRESSION)) { if(td.td_compression==(COMPRESSION)v) break; tif.tif_cleanup(tif); tif.tif_flags&=~TIF_FLAGS.TIFF_CODERSETUP; } // Setup new compression routine state. if(status=TIFFSetCompressionScheme(tif, (COMPRESSION)v)) td.td_compression=(COMPRESSION)v; else status=false; break; case TIFFTAG.PHOTOMETRIC: td.td_photometric=(PHOTOMETRIC)__GetAsUshort(ap, 0); break; case TIFFTAG.THRESHHOLDING: td.td_threshholding=(THRESHHOLD)__GetAsUshort(ap, 0); break; case TIFFTAG.FILLORDER: v=__GetAsUint(ap, 0); if((FILLORDER)v!=FILLORDER.LSB2MSB&&(FILLORDER)v!=FILLORDER.MSB2LSB) goto badvalue; td.td_fillorder=(FILLORDER)v; break; case TIFFTAG.ORIENTATION: v=__GetAsUint(ap, 0); if((ORIENTATION)v<ORIENTATION.TOPLEFT||ORIENTATION.LEFTBOT<(ORIENTATION)v) goto badvalue; td.td_orientation=(ORIENTATION)v; break; case TIFFTAG.SAMPLESPERPIXEL: // XXX should cross check -- e.g. if pallette, then 1 v=__GetAsUint(ap, 0); if(v==0) goto badvalue; td.td_samplesperpixel=(ushort)v; break; case TIFFTAG.ROWSPERSTRIP: v=__GetAsUint(ap, 0); if(v==0) goto badvalue; td.td_rowsperstrip=v; if(!TIFFFieldSet(tif, FIELD.TILEDIMENSIONS)) { td.td_tilelength=v; td.td_tilewidth=td.td_imagewidth; } break; case TIFFTAG.MINSAMPLEVALUE: td.td_minsamplevalue=__GetAsUshort(ap, 0); break; case TIFFTAG.MAXSAMPLEVALUE: td.td_maxsamplevalue=__GetAsUshort(ap, 0); break; case TIFFTAG.SMINSAMPLEVALUE: td.td_sminsamplevalue=__GetAsDouble(ap, 0); break; case TIFFTAG.SMAXSAMPLEVALUE: td.td_smaxsamplevalue=__GetAsDouble(ap, 0); break; case TIFFTAG.XRESOLUTION: td.td_xresolution=__GetAsDouble(ap, 0); break; case TIFFTAG.YRESOLUTION: td.td_yresolution=__GetAsDouble(ap, 0); break; case TIFFTAG.PLANARCONFIG: v=__GetAsUint(ap, 0); if((PLANARCONFIG)v!=PLANARCONFIG.CONTIG&&(PLANARCONFIG)v!=PLANARCONFIG.SEPARATE) goto badvalue; td.td_planarconfig=(PLANARCONFIG)v; break; case TIFFTAG.XPOSITION: td.td_xposition=__GetAsDouble(ap, 0); break; case TIFFTAG.YPOSITION: td.td_yposition=__GetAsDouble(ap, 0); break; case TIFFTAG.RESOLUTIONUNIT: v=__GetAsUint(ap, 0); if((RESUNIT)v<RESUNIT.NONE||RESUNIT.CENTIMETER<(RESUNIT)v) goto badvalue; td.td_resolutionunit=(RESUNIT)v; break; case TIFFTAG.PAGENUMBER: td.td_pagenumber[0]=__GetAsUshort(ap, 0); td.td_pagenumber[1]=__GetAsUshort(ap, 1); break; case TIFFTAG.HALFTONEHINTS: td.td_halftonehints[0]=__GetAsUshort(ap, 0); td.td_halftonehints[1]=__GetAsUshort(ap, 1); break; case TIFFTAG.COLORMAP: v=1u<<td.td_bitspersample; TIFFsetShortArray(ref td.td_colormap[0], (ushort[])ap[0], v); TIFFsetShortArray(ref td.td_colormap[1], (ushort[])ap[1], v); TIFFsetShortArray(ref td.td_colormap[2], (ushort[])ap[2], v); break; case TIFFTAG.EXTRASAMPLES: if(!setExtraSamples(td, ap, out v)) goto badvalue; break; case TIFFTAG.MATTEING: td.td_extrasamples=(ushort)((__GetAsInt(ap, 0)!=0)?1:0); if(td.td_extrasamples!=0) { ushort[] sv=new ushort[] { (ushort)EXTRASAMPLE.ASSOCALPHA }; TIFFsetShortArray(ref td.td_sampleinfo, sv, 1); } break; case TIFFTAG.TILEWIDTH: v=__GetAsUint(ap, 0); if((v%16)!=0) { if(tif.tif_mode!=O.RDONLY) goto badvalue; TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "Nonstandard tile width {0}, convert file", v); } td.td_tilewidth=v; tif.tif_flags|=TIF_FLAGS.TIFF_ISTILED; break; case TIFFTAG.TILELENGTH: v=__GetAsUint(ap, 0); if((v%16)!=0) { if(tif.tif_mode!=O.RDONLY) goto badvalue; TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "Nonstandard tile length {0}, convert file", v); } td.td_tilelength=v; tif.tif_flags|=TIF_FLAGS.TIFF_ISTILED; break; case TIFFTAG.TILEDEPTH: v=__GetAsUint(ap, 0); if(v==0) goto badvalue; td.td_tiledepth=v; break; case TIFFTAG.DATATYPE: v=__GetAsUint(ap, 0); switch(v) { case DATATYPE_VOID: td.td_sampleformat=SAMPLEFORMAT.VOID; break; case DATATYPE_INT: td.td_sampleformat=SAMPLEFORMAT.INT; break; case DATATYPE_UINT: td.td_sampleformat=SAMPLEFORMAT.UINT; break; case DATATYPE_IEEEFP: td.td_sampleformat=SAMPLEFORMAT.IEEEFP; break; default: goto badvalue; } break; case TIFFTAG.SAMPLEFORMAT: v=__GetAsUint(ap, 0); if((SAMPLEFORMAT)v<SAMPLEFORMAT.UINT||SAMPLEFORMAT.COMPLEXIEEEFP<(SAMPLEFORMAT)v) goto badvalue; td.td_sampleformat=(SAMPLEFORMAT)v; // Try to fix up the SWAB function for complex data. if(td.td_sampleformat==SAMPLEFORMAT.COMPLEXINT&&td.td_bitspersample==32&&tif.tif_postdecode==TIFFSwab32BitData) tif.tif_postdecode=TIFFSwab16BitData; else if((td.td_sampleformat==SAMPLEFORMAT.COMPLEXINT||td.td_sampleformat==SAMPLEFORMAT.COMPLEXIEEEFP)&&td.td_bitspersample==64&& tif.tif_postdecode==TIFFSwab64BitData) tif.tif_postdecode=TIFFSwab32BitData; break; case TIFFTAG.IMAGEDEPTH: td.td_imagedepth=__GetAsUint(ap, 0); break; case TIFFTAG.SUBIFD: if((tif.tif_flags&TIF_FLAGS.TIFF_INSUBIFD)==0) { td.td_nsubifd=__GetAsUshort(ap, 0); uint[] tmp=new uint[1]; tmp[0]=(uint)ap[1]; TIFFsetLongArray(ref td.td_subifd, tmp, td.td_nsubifd); } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Sorry, cannot nest SubIFDs", tif.tif_name); status=false; } break; case TIFFTAG.YCBCRPOSITIONING: td.td_ycbcrpositioning=(YCBCRPOSITION)__GetAsUshort(ap, 0); break; case TIFFTAG.YCBCRSUBSAMPLING: td.td_ycbcrsubsampling[0]=__GetAsUshort(ap, 0); td.td_ycbcrsubsampling[1]=__GetAsUshort(ap, 1); break; case TIFFTAG.TRANSFERFUNCTION: v=(uint)((td.td_samplesperpixel-td.td_extrasamples)>1?3:1); for(int i=0; i<v; i++) TIFFsetShortArray(ref td.td_transferfunction[i], (ushort[])ap[i], 1u<<td.td_bitspersample); break; case TIFFTAG.REFERENCEBLACKWHITE: // XXX should check for null range TIFFsetDoubleArray(ref td.td_refblackwhite, (double[])ap[0], 6); break; case TIFFTAG.INKNAMES: v=__GetAsUint(ap, 0); string s=(string)ap[1]; v=checkInkNamesString(tif, v, s); status=v>0; if(v>0) { td.td_inknames=s.Substring(0, (int)v); td.td_inknameslen=(int)v; } break; default: { TIFFFieldInfo fip=TIFFFindFieldInfo(tif, tag, dt); // was TIFFDataType.TIFF_ANY); TIFFTagValue tv; int iCustom; // This can happen if multiple images are open with different // codecs which have private tags. The global tag information // table may then have tags that are valid for one file but not // the other. If the client tries to set a tag that is not valid // for the image's codec then we'll arrive here. This // happens, for example, when tiffcp is used to convert between // compression schemes and codec-specific tags are blindly copied. if(fip==null||fip.field_bit!=FIELD.CUSTOM) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Invalid {1}tag \"{2}\" (not supported by codec)", tif.tif_name, isPseudoTag(tag)?"pseudo-":"", fip!=null?fip.field_name:"Unknown"); status=false; break; } // Find the existing entry for this custom value. tv=null; for(iCustom=0; iCustom<td.td_customValueCount; iCustom++) { if(td.td_customValues[iCustom].info.field_tag==tag) { tv=td.td_customValues[iCustom]; tv.value=null; break; } } // Grow the custom list if the entry was not found. if(tv==null) { try { td.td_customValueCount++; if(td.td_customValues==null) td.td_customValues=new List<TIFFTagValue>(); tv=new TIFFTagValue(); tv.info=fip; td.td_customValues.Add(tv); } catch { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Failed to allocate space for list of custom values", tif.tif_name); status=false; goto end; } } // Set custom value ... save a copy of the custom tag value. if(TIFFDataSize(fip.field_type)==0) { status=false; TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad field type {1} for \"{2}\"", tif.tif_name, fip.field_type, fip.field_name); goto end; } int apcount=0; if(fip.field_passcount) tv.count=__GetAsInt(ap, apcount++); else if(fip.field_writecount==TIFF_VARIABLE) tv.count=1; else if(fip.field_writecount==TIFF_SPP) tv.count=td.td_samplesperpixel; else tv.count=fip.field_writecount; if(fip.field_type==TIFFDataType.TIFF_ASCII) tv.value=(string)ap[apcount++]; else { if(tv.count<1) { status=false; goto end; } byte[] byteArray=null; sbyte[] sbyteArray=null; ushort[] ushortArray=null; short[] shortArray=null; uint[] uintArray=null; int[] intArray=null; float[] floatArray=null; double[] doubleArray=null; try { switch(fip.field_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: tv.value=byteArray=new byte[tv.count]; break; case TIFFDataType.TIFF_SBYTE: tv.value=sbyteArray=new sbyte[tv.count]; break; case TIFFDataType.TIFF_SHORT: tv.value=ushortArray=new ushort[tv.count]; break; case TIFFDataType.TIFF_SSHORT: tv.value=shortArray=new short[tv.count]; break; case TIFFDataType.TIFF_IFD: case TIFFDataType.TIFF_LONG: tv.value=uintArray=new uint[tv.count]; break; case TIFFDataType.TIFF_SLONG: tv.value=intArray=new int[tv.count]; break; case TIFFDataType.TIFF_FLOAT: tv.value=floatArray=new float[tv.count]; break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: case TIFFDataType.TIFF_DOUBLE: tv.value=doubleArray=new double[tv.count]; break; default: tv.value=null; break; } } catch { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Out of memory.", tif.tif_name); status=false; goto end; } bool done=false; if((fip.field_passcount||fip.field_writecount==TIFF_VARIABLE||fip.field_writecount==TIFF_SPP)) { Array a=ap[apcount] as Array; if(a!=null&&a.Length>=tv.count) { switch(fip.field_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: if(a is byte[]) { Array.Copy(a, byteArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a byte[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SBYTE: if(a is sbyte[]) { Array.Copy(a, sbyteArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a sbyte[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SHORT: if(a is ushort[]) { Array.Copy(a, ushortArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a ushort[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SSHORT: if(a is short[]) { Array.Copy(a, shortArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a short[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_IFD: case TIFFDataType.TIFF_LONG: if(a is uint[]) { Array.Copy(a, uintArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a ulong[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SLONG: if(a is int[]) { Array.Copy(a, intArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a long[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_FLOAT: if(a is float[]) { Array.Copy(a, floatArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a float[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: case TIFFDataType.TIFF_DOUBLE: if(a is double[]) { Array.Copy(a, doubleArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a double[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; default: tv.value=null; break; } // switch } // if(a!=null&&a.Length>=tv.count) else if(a!=null) { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Array has too few elements.", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } } if(!done) { try { Array a=ap[apcount] as Array; if(a!=null&&a.Length>=tv.count) { switch(fip.field_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: if(a is byte[]) { Array.Copy(a, byteArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a byte[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SBYTE: if(a is sbyte[]) { Array.Copy(a, sbyteArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a sbyte[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SHORT: if(a is ushort[]) { Array.Copy(a, ushortArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a ushort[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SSHORT: if(a is short[]) { Array.Copy(a, shortArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a short[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_IFD: case TIFFDataType.TIFF_LONG: if(a is uint[]) { Array.Copy(a, uintArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a ulong[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_SLONG: if(a is int[]) { Array.Copy(a, intArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a long[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_FLOAT: if(a is float[]) { Array.Copy(a, floatArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a float[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: case TIFFDataType.TIFF_DOUBLE: if(a is double[]) { Array.Copy(a, doubleArray, tv.count); done=true; } else { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument type for \"{2}\" ({1}). Should be a double[].", tif.tif_name, fip.field_type, fip.field_name); status=false; goto end; } break; default: tv.value=null; break; } // switch } else { switch(fip.field_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: for(int i=0; i<tv.count; i++) byteArray[i]=__GetAsByte(ap, apcount++); break; case TIFFDataType.TIFF_SBYTE: for(int i=0; i<tv.count; i++) sbyteArray[i]=__GetAsSbyte(ap, apcount++); break; case TIFFDataType.TIFF_SHORT: for(int i=0; i<tv.count; i++) ushortArray[i]=__GetAsUshort(ap, apcount++); break; case TIFFDataType.TIFF_SSHORT: for(int i=0; i<tv.count; i++) shortArray[i]=__GetAsShort(ap, apcount++); break; case TIFFDataType.TIFF_IFD: case TIFFDataType.TIFF_LONG: for(int i=0; i<tv.count; i++) uintArray[i]=__GetAsUint(ap, apcount++); break; case TIFFDataType.TIFF_SLONG: for(int i=0; i<tv.count; i++) intArray[i]=__GetAsInt(ap, apcount++); break; case TIFFDataType.TIFF_FLOAT: for(int i=0; i<tv.count; i++) floatArray[i]=__GetAsFloat(ap, apcount++); break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: case TIFFDataType.TIFF_DOUBLE: for(int i=0; i<tv.count; i++) doubleArray[i]=__GetAsDouble(ap, apcount++); break; } } } catch { TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad argument({3}) type for \"{2}\" ({1}). Should be an Array anyway!", tif.tif_name, fip.field_type, fip.field_name, apcount); status=false; goto end; } } // if(!done) } // fip.field_type!=TIFFDataType.TIFF_ASCII } // default: break; } // switch() if(status) { TIFFSetFieldBit(tif, TIFFFieldWithTag(tif, tag).field_bit); tif.tif_flags|=TIF_FLAGS.TIFF_DIRTYDIRECT; } end: return status; badvalue: TIFFErrorExt(tif.tif_clientdata, module, "{0}: Bad value {1} for \"{2}\" tag", tif.tif_name, v, TIFFFieldWithTag(tif, tag).field_name); return false; }
// Write the contents of the current directory // to the specified file. This routine doesn't // handle overwriting a directory with auxiliary // storage that's been changed. static bool TIFFWriteDirectory(TIFF tif, bool done) { if(tif.tif_mode==O.RDONLY) return true; // Clear write state so that subsequent images with // different characteristics get the right buffers // setup for them. if(done) { if((tif.tif_flags&TIF_FLAGS.TIFF_POSTENCODE)!=0) { tif.tif_flags&=~TIF_FLAGS.TIFF_POSTENCODE; if(!tif.tif_postencode(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error post-encoding before directory write"); return false; } } tif.tif_close(tif); // shutdown encoder // Flush any data that might have been written // by the compression close+cleanup routines. if(tif.tif_rawcc>0&&(tif.tif_flags&TIF_FLAGS.TIFF_BEENWRITING)!=0&&!TIFFFlushData1(tif)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error flushing data before directory write"); return false; } tif.tif_rawdata=null; tif.tif_rawcc=0; tif.tif_rawdatasize=0; tif.tif_flags&=~(TIF_FLAGS.TIFF_BEENWRITING|TIF_FLAGS.TIFF_BUFFERSETUP); } TIFFDirectory td=tif.tif_dir; // Some Irfan view versions don't like RowsPerStrip set in tiled tifs if(isTiled(tif)) TIFFClrFieldBit(tif, FIELD.ROWSPERSTRIP); // Size the directory so that we can calculate // offsets for the data items that aren't kept // in-place in each field. uint nfields=0; uint[] fields=new uint[FIELD_SETLONGS]; for(FIELD b=0; b<=(FIELD)FIELD_LAST; b++) { if(TIFFFieldSet(tif, b)&&b!=FIELD.CUSTOM) nfields+=(b<FIELD.SUBFILETYPE?2u:1u); } nfields+=(uint)td.td_customValueCount; int dirsize=(int)nfields*12; // 12: sizeof(TIFFDirEntry); List<TIFFDirEntry> dirs=null; try { dirs=new List<TIFFDirEntry>(); for(int i=0; i<nfields; i++) dirs.Add(new TIFFDirEntry()); } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Cannot write directory, out of space"); return false; } // Directory hasn't been placed yet, put // it at the end of the file and link it // into the existing directory structure. if(tif.tif_diroff==0&&!TIFFLinkDirectory(tif)) return false; //tif.tif_dataoff=(uint)(tif.tif_diroff+sizeof(uint16)+dirsize+sizeof(toff_t)); tif.tif_dataoff=(uint)(tif.tif_diroff+2+dirsize+4); if((tif.tif_dataoff&1)==1) tif.tif_dataoff++; TIFFSeekFile(tif, tif.tif_dataoff, SEEK.SET); tif.tif_curdir++; // Setup external form of directory // entries and write data items. td.td_fieldsset.CopyTo(fields, 0); // Write out ExtraSamples tag only if // extra samples are present in the data. if(FieldSet(fields, FIELD.EXTRASAMPLES)&&td.td_extrasamples==0) { ResetFieldBit(fields, FIELD.EXTRASAMPLES); nfields--; dirsize-=12; // 12: sizeof(TIFFDirEntry); } // XXX int dirsnumber=0; TIFFTAG tag; foreach(TIFFFieldInfo fip in tif.tif_fieldinfo) { TIFFDirEntry dir=(dirsnumber<dirs.Count)?dir=dirs[dirsnumber]:null; // For custom fields, we test to see if the custom field // is set or not. For normal fields, we just use the // FieldSet test. if(fip.field_bit==FIELD.CUSTOM) { bool is_set=false; for(int ci=0; ci<td.td_customValueCount; ci++) is_set|=(td.td_customValues[ci].info==fip); if(!is_set) continue; } else if(!FieldSet(fields, fip.field_bit)) continue; // Handle other fields. switch(fip.field_bit) { case FIELD.STRIPOFFSETS: // We use one field bit for both strip and tile // offsets, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEOFFSETS:TIFFTAG.STRIPOFFSETS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripoffset)) return false; break; case FIELD.STRIPBYTECOUNTS: // We use one field bit for both strip and tile // byte counts, and so must be careful in selecting // the appropriate field descriptor (so that tags // are written in sorted order). tag=isTiled(tif)?TIFFTAG.TILEBYTECOUNTS:TIFFTAG.STRIPBYTECOUNTS; if(tag!=fip.field_tag) continue; dir.tdir_tag=(ushort)tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=td.td_nstrips; if(!TIFFWriteLongArray(tif, dir, td.td_stripbytecount)) return false; break; case FIELD.ROWSPERSTRIP: TIFFSetupShortLong(tif, TIFFTAG.ROWSPERSTRIP, dir, td.td_rowsperstrip); break; case FIELD.COLORMAP: if(!TIFFWriteShortTable(tif, TIFFTAG.COLORMAP, dir, 3, td.td_colormap)) return false; break; case FIELD.IMAGEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.IMAGEWIDTH, dir, td.td_imagewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.IMAGELENGTH, dir, td.td_imagelength); break; case FIELD.TILEDIMENSIONS: TIFFSetupShortLong(tif, TIFFTAG.TILEWIDTH, dir, td.td_tilewidth); dirsnumber++; dir=dirs[dirsnumber]; TIFFSetupShortLong(tif, TIFFTAG.TILELENGTH, dir, td.td_tilelength); break; case FIELD.COMPRESSION: TIFFSetupShort(tif, TIFFTAG.COMPRESSION, dir, (ushort)td.td_compression); break; case FIELD.PHOTOMETRIC: TIFFSetupShort(tif, TIFFTAG.PHOTOMETRIC, dir, (ushort)td.td_photometric); break; case FIELD.POSITION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XPOSITION, dir, td.td_xposition)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YPOSITION, dir, td.td_yposition)) return false; break; case FIELD.RESOLUTION: if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.XRESOLUTION, dir, td.td_xresolution)) return false; dirsnumber++; dir=dirs[dirsnumber]; if(!TIFFWriteRational(tif, TIFFDataType.TIFF_RATIONAL, TIFFTAG.YRESOLUTION, dir, td.td_yresolution)) return false; break; case FIELD.BITSPERSAMPLE: case FIELD.MINSAMPLEVALUE: case FIELD.MAXSAMPLEVALUE: case FIELD.SAMPLEFORMAT: if(!TIFFWritePerSampleShorts(tif, fip.field_tag, dir)) return false; break; case FIELD.SMINSAMPLEVALUE: case FIELD.SMAXSAMPLEVALUE: if(!TIFFWritePerSampleAnys(tif, TIFFSampleToTagType(tif), fip.field_tag, dir)) return false; break; case FIELD.PAGENUMBER: case FIELD.HALFTONEHINTS: case FIELD.YCBCRSUBSAMPLING: if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; break; case FIELD.INKNAMES: if(!TIFFWriteInkNames(tif, dir)) return false; break; case FIELD.TRANSFERFUNCTION: if(!TIFFWriteTransferFunction(tif, dir)) return false; break; case FIELD.SUBIFD: // XXX: Always write this field using LONG type // for backward compatibility. dir.tdir_tag=(ushort)fip.field_tag; dir.tdir_type=(ushort)TIFFDataType.TIFF_LONG; dir.tdir_count=(uint)td.td_nsubifd; if(!TIFFWriteLongArray(tif, dir, td.td_subifd)) return false; // Total hack: if this directory includes a SubIFD // tag then force the next <n> directories to be // written as "sub directories" of this one. This // is used to write things like thumbnails and // image masks that one wants to keep out of the // normal directory linkage access mechanism. if(dir.tdir_count>0) { tif.tif_flags|=TIF_FLAGS.TIFF_INSUBIFD; tif.tif_nsubifd=(ushort)dir.tdir_count; if(dir.tdir_count>1) tif.tif_subifdoff=dir.tdir_offset; //was else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+((char*)&dir.tdir_offset-data)); else tif.tif_subifdoff=(uint)(tif.tif_diroff+2+12*dirsnumber-4); } break; default: // XXX: Should be fixed and removed. if(fip.field_tag==TIFFTAG.DOTRANGE) { if(!TIFFSetupShortPair(tif, fip.field_tag, dir)) return false; } else if(!TIFFWriteNormalTag(tif, dir, fip)) return false; break; } dirsnumber++; if(fip.field_bit!=FIELD.CUSTOM) ResetFieldBit(fields, fip.field_bit); } // Write directory. ushort dircount=(ushort)nfields; uint diroff=tif.tif_nextdiroff; if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { // The file's byte order is opposite to the // native machine architecture. We overwrite // the directory information with impunity // because it'll be released below after we // write it to the file. Note that all the // other tag construction routines assume that // we do this byte-swapping; i.e. they only // byte-swap indirect data. foreach(TIFFDirEntry dir in dirs) { TIFFSwab(ref dir.tdir_tag); TIFFSwab(ref dir.tdir_type); TIFFSwab(ref dir.tdir_count); TIFFSwab(ref dir.tdir_offset); } dircount=(ushort)nfields; TIFFSwab(ref dircount); TIFFSwab(ref diroff); } TIFFSeekFile(tif, tif.tif_diroff, SEEK.SET); if(!WriteOK(tif, dircount)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory count"); return false; } if(!WriteOK(tif, dirs, (ushort)dirsize)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory contents"); return false; } if(!WriteOK(tif, diroff)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing directory link"); return false; } if(done) { TIFFFreeDirectory(tif); tif.tif_flags&=~TIF_FLAGS.TIFF_DIRTYDIRECT; tif.tif_cleanup(tif); // Reset directory-related state for subsequent directories. TIFFCreateDirectory(tif); } return true; }