private static void Set4ByteString([NotNull] Directory directory, int tagType, [NotNull] IndexedReader reader) { var i = reader.GetUInt32(tagType); if (i != 0) { directory.Set(tagType, GetStringFromUInt32(i)); } }
private static void ProcessKodakMakernote([NotNull] KodakMakernoteDirectory directory, int tagValueOffset, [NotNull] IndexedReader reader) { // Kodak's makernote is not in IFD format. It has values at fixed offsets. var dataOffset = tagValueOffset + 8; try { directory.Set(KodakMakernoteDirectory.TagKodakModel, reader.GetString(dataOffset, 8)); directory.Set(KodakMakernoteDirectory.TagQuality, reader.GetByte(dataOffset + 9)); directory.Set(KodakMakernoteDirectory.TagBurstMode, reader.GetByte(dataOffset + 10)); directory.Set(KodakMakernoteDirectory.TagImageWidth, reader.GetUInt16(dataOffset + 12)); directory.Set(KodakMakernoteDirectory.TagImageHeight, reader.GetUInt16(dataOffset + 14)); directory.Set(KodakMakernoteDirectory.TagYearCreated, reader.GetUInt16(dataOffset + 16)); directory.Set(KodakMakernoteDirectory.TagMonthDayCreated, reader.GetBytes(dataOffset + 18, 2)); directory.Set(KodakMakernoteDirectory.TagTimeCreated, reader.GetBytes(dataOffset + 20, 4)); directory.Set(KodakMakernoteDirectory.TagBurstMode2, reader.GetUInt16(dataOffset + 24)); directory.Set(KodakMakernoteDirectory.TagShutterMode, reader.GetByte(dataOffset + 27)); directory.Set(KodakMakernoteDirectory.TagMeteringMode, reader.GetByte(dataOffset + 28)); directory.Set(KodakMakernoteDirectory.TagSequenceNumber, reader.GetByte(dataOffset + 29)); directory.Set(KodakMakernoteDirectory.TagFNumber, reader.GetUInt16(dataOffset + 30)); directory.Set(KodakMakernoteDirectory.TagExposureTime, reader.GetUInt32(dataOffset + 32)); directory.Set(KodakMakernoteDirectory.TagExposureCompensation, reader.GetInt16(dataOffset + 36)); directory.Set(KodakMakernoteDirectory.TagFocusMode, reader.GetByte(dataOffset + 56)); directory.Set(KodakMakernoteDirectory.TagWhiteBalance, reader.GetByte(dataOffset + 64)); directory.Set(KodakMakernoteDirectory.TagFlashMode, reader.GetByte(dataOffset + 92)); directory.Set(KodakMakernoteDirectory.TagFlashFired, reader.GetByte(dataOffset + 93)); directory.Set(KodakMakernoteDirectory.TagIsoSetting, reader.GetUInt16(dataOffset + 94)); directory.Set(KodakMakernoteDirectory.TagIso, reader.GetUInt16(dataOffset + 96)); directory.Set(KodakMakernoteDirectory.TagTotalZoom, reader.GetUInt16(dataOffset + 98)); directory.Set(KodakMakernoteDirectory.TagDateTimeStamp, reader.GetUInt16(dataOffset + 100)); directory.Set(KodakMakernoteDirectory.TagColorMode, reader.GetUInt16(dataOffset + 102)); directory.Set(KodakMakernoteDirectory.TagDigitalZoom, reader.GetUInt16(dataOffset + 104)); directory.Set(KodakMakernoteDirectory.TagSharpness, reader.GetSByte(dataOffset + 107)); } catch (IOException ex) { directory.AddError("Error processing Kodak makernote data: " + ex.Message); } }
/// <exception cref="System.IO.IOException"/> private static void ProcessTag(ITiffHandler handler, int tagId, int tagValueOffset, int componentCount, TiffDataFormatCode formatCode, IndexedReader reader) { switch (formatCode) { case TiffDataFormatCode.Undefined: { // this includes exif user comments handler.SetByteArray(tagId, reader.GetBytes(tagValueOffset, componentCount)); break; } case TiffDataFormatCode.String: { handler.SetString(tagId, reader.GetNullTerminatedStringValue(tagValueOffset, componentCount)); break; } case TiffDataFormatCode.RationalS: { if (componentCount == 1) { handler.SetRational(tagId, new Rational(reader.GetInt32(tagValueOffset), reader.GetInt32(tagValueOffset + 4))); } else if (componentCount > 1) { var array = new Rational[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = new Rational(reader.GetInt32(tagValueOffset + 8 * i), reader.GetInt32(tagValueOffset + 4 + 8 * i)); } handler.SetRationalArray(tagId, array); } break; } case TiffDataFormatCode.RationalU: { if (componentCount == 1) { handler.SetRational(tagId, new Rational(reader.GetUInt32(tagValueOffset), reader.GetUInt32(tagValueOffset + 4))); } else if (componentCount > 1) { var array = new Rational[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = new Rational(reader.GetUInt32(tagValueOffset + 8 * i), reader.GetUInt32(tagValueOffset + 4 + 8 * i)); } handler.SetRationalArray(tagId, array); } break; } case TiffDataFormatCode.Single: { if (componentCount == 1) { handler.SetFloat(tagId, reader.GetFloat32(tagValueOffset)); } else { var array = new float[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetFloat32(tagValueOffset + i * 4); } handler.SetFloatArray(tagId, array); } break; } case TiffDataFormatCode.Double: { if (componentCount == 1) { handler.SetDouble(tagId, reader.GetDouble64(tagValueOffset)); } else { var array = new double[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetDouble64(tagValueOffset + i * 4); } handler.SetDoubleArray(tagId, array); } break; } case TiffDataFormatCode.Int8S: { if (componentCount == 1) { handler.SetInt8S(tagId, reader.GetSByte(tagValueOffset)); } else { var array = new sbyte[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetSByte(tagValueOffset + i); } handler.SetInt8SArray(tagId, array); } break; } case TiffDataFormatCode.Int8U: { if (componentCount == 1) { handler.SetInt8U(tagId, reader.GetByte(tagValueOffset)); } else { var array = new byte[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetByte(tagValueOffset + i); } handler.SetInt8UArray(tagId, array); } break; } case TiffDataFormatCode.Int16S: { if (componentCount == 1) { handler.SetInt16S(tagId, reader.GetInt16(tagValueOffset)); } else { var array = new short[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetInt16(tagValueOffset + i * 2); } handler.SetInt16SArray(tagId, array); } break; } case TiffDataFormatCode.Int16U: { if (componentCount == 1) { handler.SetInt16U(tagId, reader.GetUInt16(tagValueOffset)); } else { var array = new ushort[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetUInt16(tagValueOffset + i * 2); } handler.SetInt16UArray(tagId, array); } break; } case TiffDataFormatCode.Int32S: { // NOTE 'long' in this case means 32 bit, not 64 if (componentCount == 1) { handler.SetInt32S(tagId, reader.GetInt32(tagValueOffset)); } else { var array = new int[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetInt32(tagValueOffset + i * 4); } handler.SetInt32SArray(tagId, array); } break; } case TiffDataFormatCode.Int32U: { // NOTE 'long' in this case means 32 bit, not 64 if (componentCount == 1) { handler.SetInt32U(tagId, reader.GetUInt32(tagValueOffset)); } else { var array = new uint[componentCount]; for (var i = 0; i < componentCount; i++) { array[i] = reader.GetUInt32(tagValueOffset + i * 4); } handler.SetInt32UArray(tagId, array); } break; } default: { handler.Error($"Invalid TIFF tag format code {formatCode} for tag 0x{tagId:X4}"); break; } } }
/// <summary>Processes a TIFF IFD.</summary> /// <remarks> /// IFD Header: /// <list type="bullet"> /// <item><b>2 bytes</b> number of tags</item> /// </list> /// Tag structure: /// <list type="bullet"> /// <item><b>2 bytes</b> tag type</item> /// <item><b>2 bytes</b> format code (values 1 to 12, inclusive)</item> /// <item><b>4 bytes</b> component count</item> /// <item><b>4 bytes</b> inline value, or offset pointer if too large to fit in four bytes</item> /// </list> /// </remarks> /// <param name="handler">the <see cref="ITiffHandler"/> that will coordinate processing and accept read values</param> /// <param name="reader">the <see cref="IndexedReader"/> from which the data should be read</param> /// <param name="processedGlobalIfdOffsets">the set of visited IFD offsets, to avoid revisiting the same IFD in an endless loop</param> /// <param name="ifdOffset">the offset within <c>reader</c> at which the IFD data starts</param> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> public static void ProcessIfd(ITiffHandler handler, IndexedReader reader, ICollection <int> processedGlobalIfdOffsets, int ifdOffset) { try { // Check for directories we've already visited to avoid stack overflows when recursive/cyclic directory structures exist. // Note that we track these offsets in the global frame, not the reader's local frame. var globalIfdOffset = reader.ToUnshiftedOffset(ifdOffset); if (processedGlobalIfdOffsets.Contains(globalIfdOffset)) { return; } // Remember that we've visited this directory so that we don't visit it again later processedGlobalIfdOffsets.Add(globalIfdOffset); // Validate IFD offset if (ifdOffset >= reader.Length || ifdOffset < 0) { handler.Error("Ignored IFD marked to start outside data segment"); return; } // First two bytes in the IFD are the number of tags in this directory int dirTagCount = reader.GetUInt16(ifdOffset); // Some software modifies the byte order of the file, but misses some IFDs (such as makernotes). // The entire test image repository doesn't contain a single IFD with more than 255 entries. // Here we detect switched bytes that suggest this problem, and temporarily swap the byte order. // This was discussed in GitHub issue #136. if (dirTagCount > 0xFF && (dirTagCount & 0xFF) == 0) { dirTagCount >>= 8; reader = reader.WithByteOrder(!reader.IsMotorolaByteOrder); } var dirLength = 2 + 12 * dirTagCount + 4; if (dirLength + ifdOffset > reader.Length) { handler.Error("Illegally sized IFD"); return; } // // Handle each tag in this directory // var invalidTiffFormatCodeCount = 0; for (var tagNumber = 0; tagNumber < dirTagCount; tagNumber++) { var tagOffset = CalculateTagOffset(ifdOffset, tagNumber); // 2 bytes for the tag id int tagId = reader.GetUInt16(tagOffset); // 2 bytes for the format code var formatCode = (TiffDataFormatCode)reader.GetUInt16(tagOffset + 2); // 4 bytes dictate the number of components in this tag's data var componentCount = reader.GetUInt32(tagOffset + 4); var format = TiffDataFormat.FromTiffFormatCode(formatCode); long byteCount; if (format == null) { if (!handler.TryCustomProcessFormat(tagId, formatCode, componentCount, out byteCount)) { // This error suggests that we are processing at an incorrect index and will generate // rubbish until we go out of bounds (which may be a while). Exit now. handler.Error($"Invalid TIFF tag format code {formatCode} for tag 0x{tagId:X4}"); // TODO specify threshold as a parameter, or provide some other external control over this behaviour if (++invalidTiffFormatCodeCount > 5) { handler.Error("Stopping processing as too many errors seen in TIFF IFD"); return; } continue; } } else { byteCount = componentCount * format.ComponentSizeBytes; } long tagValueOffset; if (byteCount > 4) { // If it's bigger than 4 bytes, the dir entry contains an offset. tagValueOffset = reader.GetUInt32(tagOffset + 8); if (tagValueOffset + byteCount > reader.Length) { // Bogus pointer offset and / or byteCount value handler.Error("Illegal TIFF tag pointer offset"); continue; } } else { // 4 bytes or less and value is in the dir entry itself. tagValueOffset = tagOffset + 8; } if (tagValueOffset < 0 || tagValueOffset > reader.Length) { handler.Error("Illegal TIFF tag pointer offset"); continue; } // Check that this tag isn't going to allocate outside the bounds of the data array. // This addresses an uncommon OutOfMemoryError. if (byteCount < 0 || tagValueOffset + byteCount > reader.Length) { handler.Error("Illegal number of bytes for TIFF tag data: " + byteCount); continue; } // Some tags point to one or more additional IFDs to process var isIfdPointer = false; if (byteCount == checked (4L * componentCount)) { for (var i = 0; i < componentCount; i++) { if (handler.TryEnterSubIfd(tagId)) { isIfdPointer = true; var subDirOffset = reader.GetUInt32((int)(tagValueOffset + i * 4)); ProcessIfd(handler, reader, processedGlobalIfdOffsets, (int)subDirOffset); } } } // If it wasn't an IFD pointer, allow custom tag processing to occur if (!isIfdPointer && !handler.CustomProcessTag((int)tagValueOffset, processedGlobalIfdOffsets, reader, tagId, (int)byteCount)) { // If no custom processing occurred, process the tag in the standard fashion ProcessTag(handler, tagId, (int)tagValueOffset, (int)componentCount, formatCode, reader); } } // at the end of each IFD is an optional link to the next IFD var finalTagOffset = CalculateTagOffset(ifdOffset, dirTagCount); var nextIfdOffset = reader.GetInt32(finalTagOffset); if (nextIfdOffset != 0) { if (nextIfdOffset >= reader.Length) { // Last 4 bytes of IFD reference another IFD with an address that is out of bounds return; } else if (nextIfdOffset < ifdOffset) { // TODO is this a valid restriction? // Last 4 bytes of IFD reference another IFD with an address that is before the start of this directory return; } if (handler.HasFollowerIfd()) { ProcessIfd(handler, reader, processedGlobalIfdOffsets, nextIfdOffset); } } } finally { handler.EndingIfd(); } }
public IEnumerable <Directory> Extract(IndexedReader reader) { var header = reader.GetUInt32(0); if (header != 0x46464600) { var flirHeaderDirectory = new FlirHeaderDirectory(); flirHeaderDirectory.AddError("Unexpected FFF header bytes."); yield return(flirHeaderDirectory); yield break; } var headerDirectory = new FlirHeaderDirectory(); headerDirectory.Set(FlirHeaderDirectory.TagCreatorSoftware, reader.GetNullTerminatedStringValue(4, 16)); yield return(headerDirectory); var baseIndexOffset = reader.GetUInt32(24); var indexCount = reader.GetUInt32(28); var indexOffset = checked ((int)baseIndexOffset); for (int i = 0; i < indexCount; i++) { var mainType = (FlirMainTagType)reader.GetUInt16(indexOffset); var subType = reader.GetUInt16(indexOffset + 2); // 1=BE, 2=LE, 3=PNG; 1 for other record types var version = reader.GetUInt32(indexOffset + 4); var id = reader.GetUInt32(indexOffset + 8); var dataOffset = reader.GetInt32(indexOffset + 12); var dataLength = reader.GetInt32(indexOffset + 16); if (mainType == FlirMainTagType.Pixels) { var reader2 = reader.WithShiftedBaseOffset(dataOffset); // should be 0x0002 if byte order is correct var marker = reader2.GetUInt16(0); if (marker > 0x0100) { reader2 = reader2.WithByteOrder(!reader2.IsMotorolaByteOrder); } var directory = new FlirRawDataDirectory(); directory.Set(FlirRawDataDirectory.TagRawThermalImageWidth, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageWidth)); directory.Set(FlirRawDataDirectory.TagRawThermalImageHeight, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageHeight)); directory.Set(FlirRawDataDirectory.TagRawThermalImageType, reader2.GetUInt16(FlirRawDataDirectory.TagRawThermalImageType)); if (ExtractRawThermalImage) { directory.Set(FlirRawDataDirectory.TagRawThermalImage, reader2.GetBytes(32, dataLength - 32)); } yield return(directory); } else if (mainType == FlirMainTagType.BasicData) { var reader2 = reader.WithShiftedBaseOffset(dataOffset); // should be 0x0002 if byte order is correct var marker = reader2.GetUInt16(0); if (marker > 0x0100) { reader2 = reader2.WithByteOrder(!reader2.IsMotorolaByteOrder); } var directory = new FlirCameraInfoDirectory(); directory.Set(TagEmissivity, reader2.GetFloat32(TagEmissivity)); directory.Set(TagObjectDistance, reader2.GetFloat32(TagObjectDistance)); directory.Set(TagReflectedApparentTemperature, reader2.GetFloat32(TagReflectedApparentTemperature)); directory.Set(TagAtmosphericTemperature, reader2.GetFloat32(TagAtmosphericTemperature)); directory.Set(TagIRWindowTemperature, reader2.GetFloat32(TagIRWindowTemperature)); directory.Set(TagIRWindowTransmission, reader2.GetFloat32(TagIRWindowTransmission)); directory.Set(TagRelativeHumidity, reader2.GetFloat32(TagRelativeHumidity)); directory.Set(TagPlanckR1, reader2.GetFloat32(TagPlanckR1)); directory.Set(TagPlanckB, reader2.GetFloat32(TagPlanckB)); directory.Set(TagPlanckF, reader2.GetFloat32(TagPlanckF)); directory.Set(TagAtmosphericTransAlpha1, reader2.GetFloat32(TagAtmosphericTransAlpha1)); directory.Set(TagAtmosphericTransAlpha2, reader2.GetFloat32(TagAtmosphericTransAlpha2)); directory.Set(TagAtmosphericTransBeta1, reader2.GetFloat32(TagAtmosphericTransBeta1)); directory.Set(TagAtmosphericTransBeta2, reader2.GetFloat32(TagAtmosphericTransBeta2)); directory.Set(TagAtmosphericTransX, reader2.GetFloat32(TagAtmosphericTransX)); directory.Set(TagCameraTemperatureRangeMax, reader2.GetFloat32(TagCameraTemperatureRangeMax)); directory.Set(TagCameraTemperatureRangeMin, reader2.GetFloat32(TagCameraTemperatureRangeMin)); directory.Set(TagCameraTemperatureMaxClip, reader2.GetFloat32(TagCameraTemperatureMaxClip)); directory.Set(TagCameraTemperatureMinClip, reader2.GetFloat32(TagCameraTemperatureMinClip)); directory.Set(TagCameraTemperatureMaxWarn, reader2.GetFloat32(TagCameraTemperatureMaxWarn)); directory.Set(TagCameraTemperatureMinWarn, reader2.GetFloat32(TagCameraTemperatureMinWarn)); directory.Set(TagCameraTemperatureMaxSaturated, reader2.GetFloat32(TagCameraTemperatureMaxSaturated)); directory.Set(TagCameraTemperatureMinSaturated, reader2.GetFloat32(TagCameraTemperatureMinSaturated)); directory.Set(TagCameraModel, reader2.GetNullTerminatedStringValue(TagCameraModel, 32)); directory.Set(TagCameraPartNumber, reader2.GetNullTerminatedStringValue(TagCameraPartNumber, 16)); directory.Set(TagCameraSerialNumber, reader2.GetNullTerminatedStringValue(TagCameraSerialNumber, 16)); directory.Set(TagCameraSoftware, reader2.GetNullTerminatedStringValue(TagCameraSoftware, 16)); directory.Set(TagLensModel, reader2.GetNullTerminatedStringValue(TagLensModel, 32)); directory.Set(TagLensPartNumber, reader2.GetNullTerminatedStringValue(TagLensPartNumber, 16)); directory.Set(TagLensSerialNumber, reader2.GetNullTerminatedStringValue(TagLensSerialNumber, 16)); directory.Set(TagFieldOfView, reader2.GetFloat32(TagFieldOfView)); directory.Set(TagFilterModel, reader2.GetNullTerminatedStringValue(TagFilterModel, 16)); directory.Set(TagFilterPartNumber, reader2.GetNullTerminatedStringValue(TagFilterPartNumber, 32)); directory.Set(TagFilterSerialNumber, reader2.GetNullTerminatedStringValue(TagFilterSerialNumber, 32)); directory.Set(TagPlanckO, reader2.GetInt32(TagPlanckO)); directory.Set(TagPlanckR2, reader2.GetFloat32(TagPlanckR2)); directory.Set(TagRawValueRangeMin, reader2.GetUInt16(TagRawValueRangeMin)); directory.Set(TagRawValueRangeMax, reader2.GetUInt16(TagRawValueRangeMax)); directory.Set(TagRawValueMedian, reader2.GetUInt16(TagRawValueMedian)); directory.Set(TagRawValueRange, reader2.GetUInt16(TagRawValueRange)); var dateTimeBytes = reader2.GetBytes(TagDateTimeOriginal, 10); var dateTimeReader = new SequentialByteArrayReader(dateTimeBytes, isMotorolaByteOrder: false); var tm = dateTimeReader.GetUInt32(); var ss = dateTimeReader.GetUInt32() & 0xffff; var tz = dateTimeReader.GetInt16(); directory.Set(TagDateTimeOriginal, new DateTimeOffset(DateUtil.FromUnixTime(tm - tz * 60).AddSeconds(ss / 1000d), TimeSpan.FromMinutes(tz))); directory.Set(TagFocusStepCount, reader2.GetUInt16(TagFocusStepCount)); directory.Set(TagFocusDistance, reader2.GetFloat32(TagFocusDistance)); directory.Set(TagFrameRate, reader2.GetUInt16(TagFrameRate)); yield return(directory); } indexOffset += 32; } }