private static void printField(Stream fd, TiffFieldInfo fip, int value_count, object raw_data) { fprintf(fd, " {0}: ", fip.Name); byte[] bytes = raw_data as byte[]; sbyte[] sbytes = raw_data as sbyte[]; short[] shorts = raw_data as short[]; ushort[] ushorts = raw_data as ushort[]; int[] ints = raw_data as int[]; uint[] uints = raw_data as uint[]; float[] floats = raw_data as float[]; double[] doubles = raw_data as double[]; string s = raw_data as string; for (int j = 0; j < value_count; j++) { if (fip.Type == TiffType.Byte || fip.Type == TiffType.SByte) { if (bytes != null) fprintf(fd, "{0}", bytes[j]); else if (sbytes != null) fprintf(fd, "{0}", sbytes[j]); } else if (fip.Type == TiffType.Undefined) { if (bytes != null) fprintf(fd, "0x{0:x}", bytes[j]); } else if (fip.Type == TiffType.Short || fip.Type == TiffType.SShort) { if (shorts != null) fprintf(fd, "{0}", shorts[j]); else if (ushorts != null) fprintf(fd, "{0}", ushorts[j]); } else if (fip.Type == TiffType.Long || fip.Type == TiffType.SLong) { if (ints != null) fprintf(fd, "{0}", ints[j]); else if (uints != null) fprintf(fd, "{0}", uints[j]); } else if (fip.Type == TiffType.Rational || fip.Type == TiffType.SRational || fip.Type == TiffType.Float) { if (floats != null) fprintf(fd, "{0}", floats[j]); } else if (fip.Type == TiffType.IFD) { if (ints != null) fprintf(fd, "0x{0:x}", ints[j]); else if (uints != null) fprintf(fd, "0x{0:x}", uints[j]); } else if (fip.Type == TiffType.ASCII) { if (s != null) fprintf(fd, "{0}", s); break; } else if (fip.Type == TiffType.Double || fip.Type == TiffType.Float) { if (floats != null) fprintf(fd, "{0}", floats[j]); else if (doubles != null) fprintf(fd, "{0}", doubles[j]); } else { fprintf(fd, "<unsupported data type in printField>"); break; } if (j < value_count - 1) fprintf(fd, ","); } fprintf(fd, "\r\n"); }
/// <summary> /// Reads the contents of the next TIFF directory in an open TIFF file/stream and makes /// it the current directory. /// </summary> /// <returns><c>true</c> if directory was successfully read; otherwise, <c>false</c> if an /// error was encountered, or if there are no more directories to be read.</returns> /// <remarks><para>Directories are read sequentially.</para> /// <para>Applications only need to call <see cref="ReadDirectory"/> to read multiple /// subfiles in a single TIFF file/stream - the first directory in a file/stream is /// automatically read when <see cref="Open"/> or <see cref="ClientOpen"/> is called. /// </para><para> /// The images that have a single uncompressed strip or tile of data are automatically /// treated as if they were made up of multiple strips or tiles of approximately 8 /// kilobytes each. This operation is done only in-memory; it does not alter the contents /// of the file/stream. However, the construction of the "chopped strips" is visible to /// the application through the number of strips returned by <see cref="NumberOfStrips"/> /// or the number of tiles returned by <see cref="NumberOfTiles"/>.</para> /// </remarks> public bool ReadDirectory() { const string module = "ReadDirectory"; m_diroff = m_nextdiroff; if (m_diroff == 0) { // no more directories return false; } // Check whether we have the last offset or bad offset (IFD looping). if (!checkDirOffset(m_nextdiroff)) return false; // Cleanup any previous compression state. m_currentCodec.Cleanup(); m_curdir++; TiffDirEntry[] dir; short dircount = fetchDirectory(m_nextdiroff, out dir, out m_nextdiroff); if (dircount == 0) { ErrorExt(this, m_clientdata, module, "{0}: Failed to read directory at offset {1}", m_name, m_nextdiroff); return false; } // reset before new dir m_flags &= ~TiffFlags.BeenWriting; // 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. // free any old stuff and reinit FreeDirectory(); setupDefaultDirectory(); // 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. SetField(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. for (int i = 0; i < dircount; i++) { TiffDirEntry dp = dir[i]; if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab) { short temp = (short)dp.tdir_tag; SwabShort(ref temp); dp.tdir_tag = (TiffTag)(ushort)temp; temp = (short)dp.tdir_type; SwabShort(ref temp); dp.tdir_type = (TiffType)temp; SwabLong(ref dp.tdir_count); SwabUInt(ref dp.tdir_offset); } if (dp.tdir_tag == TiffTag.SamplesPerPixel) { if (!fetchNormalTag(dir[i])) return false; dp.tdir_tag = TiffTag.Ignore; } } // First real pass over the directory. int fix = 0; bool diroutoforderwarning = false; bool haveunknowntags = false; for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.Ignore) continue; if (fix >= m_nfields) 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 (dir[i].tdir_tag < m_fieldinfo[fix].Tag) { if (!diroutoforderwarning) { WarningExt(this, m_clientdata, module, "{0}: invalid TIFF directory; tags are not sorted in ascending order", m_name); diroutoforderwarning = true; } fix = 0; // O(n^2) } while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { // Unknown tag ... we'll deal with it below haveunknowntags = true; continue; } // null out old tags that we ignore. if (m_fieldinfo[fix].Bit == FieldBit.Ignore) { dir[i].tdir_tag = TiffTag.Ignore; continue; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.Any) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.Ignore; continue; } } // Check count if known in advance. if (fip.ReadCount != TiffFieldInfo.Variable && fip.ReadCount != TiffFieldInfo.Variable2) { int expected = fip.ReadCount; if (fip.ReadCount == TiffFieldInfo.Spp) expected = m_dir.td_samplesperpixel; if (!checkDirCount(dir[i], expected)) { dir[i].tdir_tag = TiffTag.Ignore; continue; } } switch (dir[i].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 (dir[i].tdir_count == 1) { int v = extractData(dir[i]); if (!SetField(dir[i].tdir_tag, v)) return false; break; // XXX: workaround for broken TIFFs } else if (dir[i].tdir_type == TiffType.Long) { int v; if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) return false; } else { short iv; if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) return false; } dir[i].tdir_tag = TiffTag.Ignore; break; case TiffTag.StripOffsets: case TiffTag.StripByteCounts: case TiffTag.TileOffsets: case TiffTag.TileByteCounts: setFieldBit(fip.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 (!fetchNormalTag(dir[i])) return false; dir[i].tdir_tag = TiffTag.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; for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.Ignore) continue; if (fix >= m_nfields || dir[i].tdir_tag < m_fieldinfo[fix].Tag) { // O(n^2) fix = 0; } while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { Tiff.WarningExt(this, m_clientdata, module, "{0}: unknown field with tag {1} (0x{2:x}) encountered", m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); TiffFieldInfo[] arr = new TiffFieldInfo[1]; arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); MergeFieldInfo(arr, 1); fix = 0; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.Any) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { Tiff.WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.Ignore; break; } } } } // XXX: OJPEG hack. // If a) compression is OJPEG, b) planarconfig tag says it's separate, c) strip // offsets/bytecounts tag are both present and d) both contain exactly one value, then // we consistently find that the buggy implementation of the buggy compression scheme // matches contig planarconfig best. So we 'fix-up' the tag here if ((m_dir.td_compression == Compression.OJPEG) && (m_dir.td_planarconfig == PlanarConfig.Separate)) { int dpIndex = readDirectoryFind(dir, dircount, TiffTag.StripOffsets); if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) { dpIndex = readDirectoryFind(dir, dircount, TiffTag.StripByteCounts); if (dpIndex != -1 && dir[dpIndex].tdir_count == 1) { m_dir.td_planarconfig = PlanarConfig.Contig; WarningExt(this, m_clientdata, "ReadDirectory", "Planarconfig tag value assumed incorrect, assuming data is contig instead of chunky"); } } } // Allocate directory structure and setup defaults. if (!fieldSet(FieldBit.ImageDimensions)) { missingRequired("ImageLength"); return false; } // Setup appropriate structures (by strip or by tile) if (!fieldSet(FieldBit.TileDimensions)) { m_dir.td_nstrips = NumberOfStrips(); m_dir.td_tilewidth = m_dir.td_imagewidth; m_dir.td_tilelength = m_dir.td_rowsperstrip; m_dir.td_tiledepth = m_dir.td_imagedepth; m_flags &= ~TiffFlags.IsTiled; } else { m_dir.td_nstrips = NumberOfTiles(); m_flags |= TiffFlags.IsTiled; } if (m_dir.td_nstrips == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero number of {1}", m_name, IsTiled() ? "tiles" : "strips"); return false; } m_dir.td_stripsperimage = m_dir.td_nstrips; if (m_dir.td_planarconfig == PlanarConfig.Separate) m_dir.td_stripsperimage /= m_dir.td_samplesperpixel; if (!fieldSet(FieldBit.StripOffsets)) { if ((m_dir.td_compression == Compression.OJPEG) && !IsTiled() && (m_dir.td_nstrips == 1)) { // XXX: OJPEG hack. // If a) compression is OJPEG, b) it's not a tiled TIFF, and c) the number of // strips is 1, then we tolerate the absence of stripoffsets tag, because, // presumably, all required data is in the JpegInterchangeFormat stream. setFieldBit(FieldBit.StripOffsets); } else { missingRequired(IsTiled() ? "TileOffsets" : "StripOffsets"); return false; } } // Second pass: extract other information. for (int i = 0; i < dircount; i++) { if (dir[i].tdir_tag == TiffTag.Ignore) continue; switch (dir[i].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 (dir[i].tdir_count == 1) { int v = extractData(dir[i]); if (!SetField(dir[i].tdir_tag, v)) return false; // XXX: workaround for broken TIFFs } else if (dir[i].tdir_tag == TiffTag.BitsPerSample && dir[i].tdir_type == TiffType.Long) { int v; if (!fetchPerSampleLongs(dir[i], out v) || !SetField(dir[i].tdir_tag, v)) return false; } else { short iv; if (!fetchPerSampleShorts(dir[i], out iv) || !SetField(dir[i].tdir_tag, iv)) return false; } break; case TiffTag.SMinSampleValue: case TiffTag.SMaxSampleValue: double dv; if (!fetchPerSampleAnys(dir[i], out dv) || !SetField(dir[i].tdir_tag, dv)) return false; break; case TiffTag.StripOffsets: case TiffTag.TileOffsets: if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.td_stripoffset)) return false; break; case TiffTag.StripByteCounts: case TiffTag.TileByteCounts: if (!fetchStripThing(dir[i], m_dir.td_nstrips, ref m_dir.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. int v = 1 << m_dir.td_bitspersample; if (dir[i].tdir_tag == TiffTag.Colormap || dir[i].tdir_count != v) { if (!checkDirCount(dir[i], 3 * v)) break; } byte[] cp = new byte[dir[i].tdir_count * sizeof(short)]; if (fetchData(dir[i], cp) != 0) { int c = 1 << m_dir.td_bitspersample; if (dir[i].tdir_count == c) { // This deals with there being only one array to apply to all samples. short[] u = ByteArrayToShorts(cp, 0, dir[i].tdir_count * sizeof(short)); SetField(dir[i].tdir_tag, u, u, u); } else { v *= sizeof(short); short[] u0 = ByteArrayToShorts(cp, 0, v); short[] u1 = ByteArrayToShorts(cp, v, v); short[] u2 = ByteArrayToShorts(cp, 2 * v, v); SetField(dir[i].tdir_tag, u0, u1, u2); } } break; } case TiffTag.PageNumber: case TiffTag.HalfToneHints: case TiffTag.YCBCRSUBSAMPLING: case TiffTag.DotRange: fetchShortPair(dir[i]); break; case TiffTag.REFERENCEBLACKWHITE: fetchRefBlackWhite(dir[i]); break; // BEGIN REV 4.0 COMPATIBILITY case TiffTag.OSubFileType: FileType ft = 0; switch ((SubFileType)extractData(dir[i])) { case SubFileType.ReducedSizeImage: ft = FileType.ReducedResImage; break; case SubFileType.Page: ft = FileType.Page; break; } if (ft != 0) SetField(TiffTag.SubFileType, ft); break; // END REV 4.0 COMPATIBILITY default: fetchNormalTag(dir[i]); break; } } // OJPEG hack: // - If a) compression is OJPEG, and b) photometric tag is missing, then we // consistently find that photometric should be YCbCr // - If a) compression is OJPEG, and b) photometric tag says it's RGB, then we // consistently find that the buggy implementation of the buggy compression scheme // matches photometric YCbCr instead. // - If a) compression is OJPEG, and b) bitspersample tag is missing, then we // consistently find bitspersample should be 8. // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric // is RGB or YCbCr, then we consistently find samplesperpixel should be 3 // - If a) compression is OJPEG, b) samplesperpixel tag is missing, and c) photometric // is MinIsWhite or MinIsBlack, then we consistently find samplesperpixel should be 3 if (m_dir.td_compression == Compression.OJPEG) { if (!fieldSet(FieldBit.Photometric)) { WarningExt(this, m_clientdata, "ReadDirectory", "Photometric tag is missing, assuming data is YCbCr"); if (!SetField(TiffTag.Photometric, Photometric.YCBCR)) return false; } else if (m_dir.td_photometric == Photometric.RGB) { m_dir.td_photometric = Photometric.YCBCR; WarningExt(this, m_clientdata, "ReadDirectory", "Photometric tag value assumed incorrect, assuming data is YCbCr instead of RGB"); } if (!fieldSet(FieldBit.BitsPerSample)) { WarningExt(this, m_clientdata, "ReadDirectory", "BitsPerSample tag is missing, assuming 8 bits per sample"); if (!SetField(TiffTag.BitsPerSample, 8)) return false; } if (!fieldSet(FieldBit.SamplesPerPixel)) { if ((m_dir.td_photometric == Photometric.RGB) || (m_dir.td_photometric == Photometric.YCBCR)) { WarningExt(this, m_clientdata, "ReadDirectory", "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 3"); if (!SetField(TiffTag.SamplesPerPixel, 3)) return false; } else if ((m_dir.td_photometric == Photometric.MinIsWhite) || (m_dir.td_photometric == Photometric.MinIsBlack)) { WarningExt(this, m_clientdata, "ReadDirectory", "SamplesPerPixel tag is missing, assuming correct SamplesPerPixel value is 1"); if (!SetField(TiffTag.SamplesPerPixel, 1)) return false; } } } // Verify Palette image has a Colormap. if (m_dir.td_photometric == Photometric.Palette && !fieldSet(FieldBit.ColorMap)) { missingRequired("Colormap"); return false; } // OJPEG hack: // We do no further messing with strip/tile offsets/bytecounts in OJPEG TIFFs if (m_dir.td_compression != Compression.OJPEG) { // Attempt to deal with a missing StripByteCounts tag. if (!fieldSet(FieldBit.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 ((m_dir.td_planarconfig == PlanarConfig.Contig && m_dir.td_nstrips > 1) || (m_dir.td_planarconfig == PlanarConfig.Separate && m_dir.td_nstrips != m_dir.td_samplesperpixel)) { missingRequired("StripByteCounts"); return false; } WarningExt(this, m_clientdata, module, "{0}: TIFF directory is missing required \"{1}\" field, calculating from imagelength", m_name, FieldWithTag(TiffTag.StripByteCounts).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } else if (m_dir.td_nstrips == 1 && m_dir.td_stripoffset[0] != 0 && byteCountLooksBad(m_dir)) { // 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. WarningExt(this, m_clientdata, module, "{0}: Bogus \"{1}\" field, ignoring and calculating from imagelength", m_name, FieldWithTag(TiffTag.StripByteCounts).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } else if (m_dir.td_planarconfig == PlanarConfig.Contig && m_dir.td_nstrips > 2 && m_dir.td_compression == Compression.None && m_dir.td_stripbytecount[0] != m_dir.td_stripbytecount[1]) { // XXX: Some vendors fill StripByteCount array with absolutely wrong values // (it can be equal to StripOffset array, for example). Catch this case here. WarningExt(this, m_clientdata, module, "{0}: Wrong \"{1}\" field, ignoring and calculating from imagelength", m_name, FieldWithTag(TiffTag.StripByteCounts).Name); if (!estimateStripByteCounts(dir, dircount)) return false; } } dir = null; if (!fieldSet(FieldBit.MaxSampleValue)) m_dir.td_maxsamplevalue = (short)((1 << m_dir.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 appendToStrip() function. if (m_dir.td_nstrips > 1) { m_dir.td_stripbytecountsorted = true; for (int strip = 1; strip < m_dir.td_nstrips; strip++) { if (m_dir.td_stripoffset[strip - 1] > m_dir.td_stripoffset[strip]) { m_dir.td_stripbytecountsorted = false; break; } } } if (!fieldSet(FieldBit.Compression)) SetField(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 (m_dir.td_nstrips == 1 && m_dir.td_compression == Compression.None && (m_flags & TiffFlags.StripChop) == TiffFlags.StripChop && (m_flags & TiffFlags.IsTiled) != TiffFlags.IsTiled) { chopUpSingleUncompressedStrip(); } // Reinitialize i/o since we are starting on a new directory. m_row = -1; m_curstrip = -1; m_col = -1; m_curtile = -1; m_tilesize = -1; m_scanlinesize = ScanlineSize(); if (m_scanlinesize == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero scanline size", m_name); return false; } if (IsTiled()) { m_tilesize = TileSize(); if (m_tilesize == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero tile size", m_name); return false; } } else { if (StripSize() == 0) { ErrorExt(this, m_clientdata, module, "{0}: cannot handle zero strip size", m_name); return false; } } return true; }
/// <summary> /// Reads a custom directory from the arbitrary offset within file/stream. /// </summary> /// <param name="offset">The directory offset.</param> /// <param name="info">The array of <see cref="TiffFieldInfo"/> objects to merge to /// existing field information.</param> /// <param name="count">The number of items to use from /// the <paramref name="info"/> array.</param> /// <returns><c>true</c> if a custom directory was read successfully; /// otherwise, <c>false</c></returns> public bool ReadCustomDirectory(long offset, TiffFieldInfo[] info, int count) { const string module = "ReadCustomDirectory"; setupFieldInfo(info, count); uint dummyNextDirOff; TiffDirEntry[] dir; short dircount = fetchDirectory((uint)offset, out dir, out dummyNextDirOff); if (dircount == 0) { ErrorExt(this, m_clientdata, module, "{0}: Failed to read custom directory at offset {1}", m_name, offset); return false; } FreeDirectory(); m_dir = new TiffDirectory(); int fix = 0; for (short i = 0; i < dircount; i++) { if ((m_flags & TiffFlags.Swab) == TiffFlags.Swab) { short temp = (short)dir[i].tdir_tag; SwabShort(ref temp); dir[i].tdir_tag = (TiffTag)(ushort)temp; temp = (short)dir[i].tdir_type; SwabShort(ref temp); dir[i].tdir_type = (TiffType)temp; SwabLong(ref dir[i].tdir_count); SwabUInt(ref dir[i].tdir_offset); } if (fix >= m_nfields || dir[i].tdir_tag == TiffTag.Ignore) continue; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; if (fix >= m_nfields || m_fieldinfo[fix].Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: unknown field with tag {1} (0x{2:x}) encountered", m_name, (ushort)dir[i].tdir_tag, (ushort)dir[i].tdir_tag); TiffFieldInfo[] arr = new TiffFieldInfo[1]; arr[0] = createAnonFieldInfo(dir[i].tdir_tag, dir[i].tdir_type); MergeFieldInfo(arr, 1); fix = 0; while (fix < m_nfields && m_fieldinfo[fix].Tag < dir[i].tdir_tag) fix++; } // null out old tags that we ignore. if (m_fieldinfo[fix].Bit == FieldBit.Ignore) { dir[i].tdir_tag = TiffTag.Ignore; continue; } // Check data type. TiffFieldInfo fip = m_fieldinfo[fix]; while (dir[i].tdir_type != fip.Type && fix < m_nfields) { if (fip.Type == TiffType.Any) { // wildcard break; } fip = m_fieldinfo[++fix]; if (fix >= m_nfields || fip.Tag != dir[i].tdir_tag) { WarningExt(this, m_clientdata, module, "{0}: wrong data type {1} for \"{2}\"; tag ignored", m_name, dir[i].tdir_type, m_fieldinfo[fix - 1].Name); dir[i].tdir_tag = TiffTag.Ignore; continue; } } // Check count if known in advance. if (fip.ReadCount != TiffFieldInfo.Variable && fip.ReadCount != TiffFieldInfo.Variable2) { int expected = fip.ReadCount; if (fip.ReadCount == TiffFieldInfo.Spp) expected = m_dir.td_samplesperpixel; if (!checkDirCount(dir[i], expected)) { dir[i].tdir_tag = TiffTag.Ignore; continue; } } // EXIF tags which need to be specifically processed. switch (dir[i].tdir_tag) { case TiffTag.EXIF_SUBJECTDISTANCE: fetchSubjectDistance(dir[i]); break; default: fetchNormalTag(dir[i]); break; } } return true; }
/// <summary> /// Merges given field information to existing one. /// </summary> /// <param name="info">The array of <see cref="TiffFieldInfo"/> objects.</param> /// <param name="count">The number of items to use from the <paramref name="info"/> array.</param> public void MergeFieldInfo(TiffFieldInfo[] info, int count) { m_foundfield = null; if (m_nfields > 0) m_fieldinfo = Realloc(m_fieldinfo, m_nfields, m_nfields + count); else m_fieldinfo = new TiffFieldInfo[count]; for (int i = 0; i < count; i++) { TiffFieldInfo fip = FindFieldInfo(info[i].Tag, info[i].Type); // only add definitions that aren't already present if (fip == null) { m_fieldinfo[m_nfields] = info[i]; m_nfields++; } } // Sort the field info by tag number IComparer myComparer = new TagCompare(); Array.Sort(m_fieldinfo, 0, m_nfields, myComparer); }
/// <summary> /// Retrieves field information for the tag with specified name. /// </summary> /// <param name="name">The name of the tag to retrieve field information for.</param> /// <param name="type">The tiff data type to use us additional filter.</param> /// <returns>The field information for specified tag with specified type or <c>null</c> if /// the field information wasn't found.</returns> public TiffFieldInfo FindFieldInfoByName(string name, TiffType type) { if (m_foundfield != null && m_foundfield.Name == name && (type == TiffType.Any || type == m_foundfield.Type)) { return m_foundfield; } // If we are invoked with no field information, then just return. if (m_fieldinfo == null) return null; m_foundfield = null; foreach (TiffFieldInfo info in m_fieldinfo) { if (info != null && info.Name == name && (type == TiffType.Any || type == info.Type)) { m_foundfield = info; break; } } return m_foundfield; }
/* * Setup a default directory structure. */ private void setupDefaultDirectory() { int tiffFieldInfoCount; TiffFieldInfo[] tiffFieldInfo = getFieldInfo(out tiffFieldInfoCount); setupFieldInfo(tiffFieldInfo, tiffFieldInfoCount); m_dir = new TiffDirectory(); m_postDecodeMethod = PostDecodeMethodType.pdmNone; m_foundfield = null; m_tagmethods = m_defaultTagMethods; /* * Give client code a chance to install their own * tag extensions & methods, prior to compression overloads. */ if (m_extender != null) m_extender(this); SetField(TiffTag.Compression, Compression.None); /* * NB: The directory is marked dirty as a result of setting * up the default compression scheme. However, this really * isn't correct -- we want DirtyDirect to be set only * if the user does something. We could just do the setup * by hand, but it seems better to use the normal mechanism * (i.e. SetField). */ m_flags &= ~TiffFlags.DirtyDirect; /* * we clear the IsTiled flag when setting up a new directory. * Should we also be clearing stuff like InSubIFD? */ m_flags &= ~TiffFlags.IsTiled; /* * Clear other directory-specific fields. */ m_tilesize = -1; m_scanlinesize = -1; }
private static TiffFieldInfo createAnonFieldInfo(TiffTag tag, TiffType field_type) { TiffFieldInfo fld = new TiffFieldInfo(tag, TiffFieldInfo.Variable2, TiffFieldInfo.Variable2, field_type, FieldBit.Custom, true, true, null); // note that this name is a special sign to Close() and // setupFieldInfo() to free the field fld.Name = string.Format(CultureInfo.InvariantCulture, "Tag {0}", tag); return fld; }
private void setupFieldInfo(TiffFieldInfo[] info, int n) { m_nfields = 0; MergeFieldInfo(info, n); }
/// <summary> /// Writes tags that are not special cased. /// </summary> private bool writeNormalTag(ref TiffDirEntry dir, TiffFieldInfo fip) { short wc = fip.WriteCount; dir.tdir_tag = fip.Tag; dir.tdir_type = fip.Type; dir.tdir_count = wc; switch (fip.Type) { case TiffType.Short: case TiffType.SShort: if (fip.PassCount) { short[] wp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); wp = result[1].ToShortArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); wp = result[1].ToShortArray(); dir.tdir_count = wc; } if (!writeShortArray(ref dir, wp)) return false; } else { if (wc == 1) { FieldValue[] result = GetField(fip.Tag); short sv = result[0].ToShort(); dir.tdir_offset = insertData(dir.tdir_type, sv); } else { FieldValue[] result = GetField(fip.Tag); short[] wp = result[0].ToShortArray(); if (!writeShortArray(ref dir, wp)) return false; } } break; case TiffType.Long: case TiffType.SLong: case TiffType.IFD: if (fip.PassCount) { int[] lp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); lp = result[1].ToIntArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); lp = result[1].ToIntArray(); dir.tdir_count = wc; } if (!writeLongArray(ref dir, lp)) return false; } else { if (wc == 1) { // XXX handle Long->Short conversion FieldValue[] result = GetField(fip.Tag); dir.tdir_offset = result[0].ToUInt(); } else { int[] lp; FieldValue[] result = GetField(fip.Tag); lp = result[0].ToIntArray(); if (!writeLongArray(ref dir, lp)) return false; } } break; case TiffType.Rational: case TiffType.SRational: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeRationalArray(ref dir, fp)) return false; } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeRationalArray(ref dir, fv)) return false; } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeRationalArray(ref dir, fp)) return false; } } break; case TiffType.Float: if (fip.PassCount) { float[] fp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); fp = result[1].ToFloatArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); fp = result[1].ToFloatArray(); dir.tdir_count = wc; } if (!writeFloatArray(ref dir, fp)) return false; } else { if (wc == 1) { float[] fv = new float[1]; FieldValue[] result = GetField(fip.Tag); fv[0] = result[0].ToFloat(); if (!writeFloatArray(ref dir, fv)) return false; } else { FieldValue[] result = GetField(fip.Tag); float[] fp = result[0].ToFloatArray(); if (!writeFloatArray(ref dir, fp)) return false; } } break; case TiffType.Double: if (fip.PassCount) { double[] dp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); dp = result[1].ToDoubleArray(); dir.tdir_count = wc; } if (!writeDoubleArray(ref dir, dp)) return false; } else { if (wc == 1) { double[] dv = new double[1]; FieldValue[] result = GetField(fip.Tag); dv[0] = result[0].ToDouble(); if (!writeDoubleArray(ref dir, dv)) return false; } else { FieldValue[] result = GetField(fip.Tag); double[] dp = result[0].ToDoubleArray(); if (!writeDoubleArray(ref dir, dp)) return false; } } break; case TiffType.ASCII: { FieldValue[] result = GetField(fip.Tag); string cp; if (fip.PassCount) cp = result[1].ToString(); else cp = result[0].ToString(); // add zero ('\0') at the end of the byte array byte[] stringBytes = Latin1Encoding.GetBytes(cp); byte[] totalBytes = new byte[stringBytes.Length + 1]; Buffer.BlockCopy(stringBytes, 0, totalBytes, 0, stringBytes.Length); dir.tdir_count = totalBytes.Length; if (!writeByteArray(ref dir, totalBytes)) return false; } break; case TiffType.Byte: case TiffType.SByte: if (fip.PassCount) { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { // Assume TiffFieldInfo.Variable FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } if (!writeByteArray(ref dir, cp)) return false; } else { if (wc == 1) { byte[] cv = new byte[1]; FieldValue[] result = GetField(fip.Tag); cv[0] = result[0].ToByte(); if (!writeByteArray(ref dir, cv)) return false; } else { FieldValue[] result = GetField(fip.Tag); byte[] cp = result[0].ToByteArray(); if (!writeByteArray(ref dir, cp)) return false; } } break; case TiffType.Undefined: { byte[] cp; int wc2; if (wc == TiffFieldInfo.Variable) { FieldValue[] result = GetField(fip.Tag); wc = result[0].ToShort(); cp = result[1].ToByteArray(); dir.tdir_count = wc; } else if (wc == TiffFieldInfo.Variable2) { FieldValue[] result = GetField(fip.Tag); wc2 = result[0].ToInt(); cp = result[1].ToByteArray(); dir.tdir_count = wc2; } else { FieldValue[] result = GetField(fip.Tag); cp = result[0].ToByteArray(); } if (!writeByteArray(ref dir, cp)) return false; } break; case TiffType.NoType: break; } return true; }
internal static TiffFieldInfo[] Realloc(TiffFieldInfo[] buffer, int elementCount, int newElementCount) { TiffFieldInfo[] newBuffer = new TiffFieldInfo[newElementCount]; if (buffer != null) { int copyLength = Math.Min(elementCount, newElementCount); Array.Copy(buffer, newBuffer, copyLength); } return newBuffer; }