/// <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> /// Releases storage associated with current directory. /// </summary> public void FreeDirectory() { if (m_dir != null) { clearFieldBit(FieldBit.YCbCrSubsampling); clearFieldBit(FieldBit.YCbCrPositioning); m_dir = null; } }
private static void defaultRefBlackWhite(TiffDirectory td) { td.td_refblackwhite = new float[6]; if (td.td_photometric == Photometric.YCBCR) { // YCbCr (Class Y) images must have the ReferenceBlackWhite tag set. Fix the // broken images, which lacks that tag. td.td_refblackwhite[0] = 0.0F; td.td_refblackwhite[1] = td.td_refblackwhite[3] = td.td_refblackwhite[5] = 255.0F; td.td_refblackwhite[2] = td.td_refblackwhite[4] = 128.0F; } else { // Assume RGB (Class R) for (int i = 0; i < 3; i++) { td.td_refblackwhite[2 * i + 0] = 0; td.td_refblackwhite[2 * i + 1] = (float)((1L << td.td_bitspersample) - 1L); } } }
private static bool defaultTransferFunction(TiffDirectory td) { short[][] tf = td.td_transferfunction; tf[0] = null; tf[1] = null; tf[2] = null; if (td.td_bitspersample >= sizeof(int) * 8 - 2) return false; int n = 1 << td.td_bitspersample; tf[0] = new short[n]; tf[0][0] = 0; for (int i = 1; i < n; i++) { double t = (double)i / ((double)n - 1.0); tf[0][i] = (short)Math.Floor(65535.0 * Math.Pow(t, 2.2) + 0.5); } if (td.td_samplesperpixel - td.td_extrasamples > 1) { tf[1] = new short[n]; Buffer.BlockCopy(tf[0], 0, tf[1], 0, tf[0].Length * sizeof(short)); tf[2] = new short[n]; Buffer.BlockCopy(tf[0], 0, tf[2], 0, tf[0].Length * sizeof(short)); } return true; }
/* * 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; }
/// <summary> /// Install extra samples information. /// </summary> private static bool setExtraSamples(TiffDirectory td, ref int v, FieldValue[] ap) { // XXX: Unassociated alpha data == 999 is a known Corel Draw bug, see below const short EXTRASAMPLE_COREL_UNASSALPHA = 999; v = ap[0].ToInt(); if (v > td.td_samplesperpixel) return false; byte[] va = ap[1].ToByteArray(); if (v > 0 && va == null) { // typically missing param return false; } for (int i = 0; i < v; i++) { if ((ExtraSample)va[i] > ExtraSample.UnAssociatedAlpha) { // XXX: Corel Draw is known to produce incorrect // ExtraSamples tags which must be patched here if we // want to be able to open some of the damaged TIFF files: if (i < v - 1) { short s = BitConverter.ToInt16(va, i); if (s == EXTRASAMPLE_COREL_UNASSALPHA) va[i] = (byte)ExtraSample.UnAssociatedAlpha; } else return false; } } td.td_extrasamples = (short)v; td.td_sampleinfo = new ExtraSample[td.td_extrasamples]; for (int i = 0; i < td.td_extrasamples; i++) td.td_sampleinfo[i] = (ExtraSample)va[i]; return true; }
private bool byteCountLooksBad(TiffDirectory td) { /* * 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. */ return ( (td.td_stripbytecount[0] == 0 && td.td_stripoffset[0] != 0) || (td.td_compression == Compression.None && td.td_stripbytecount[0] > getFileSize() - td.td_stripoffset[0]) || (m_mode == O_RDONLY && td.td_compression == Compression.None && td.td_stripbytecount[0] < ScanlineSize() * td.td_imagelength) ); }