private static void ProcessReconyxMakernote([NotNull] ReconyxMakernoteDirectory directory, int makernoteOffset, [NotNull] IndexedReader reader) { directory.Set(ReconyxMakernoteDirectory.TagMakernoteVersion, reader.GetUInt16(makernoteOffset)); // revision and build are reversed from .NET ordering ushort major = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagFirmwareVersion); ushort minor = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagFirmwareVersion + 2); ushort revision = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagFirmwareVersion + 4); string buildYear = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagFirmwareVersion + 6).ToString("x4"); string buildDate = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagFirmwareVersion + 8).ToString("x4"); int build = int.Parse(buildYear + buildDate); directory.Set(ReconyxMakernoteDirectory.TagFirmwareVersion, new Version(major, minor, revision, build)); directory.Set(ReconyxMakernoteDirectory.TagTriggerMode, new string((char)reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagTriggerMode), 1)); directory.Set(ReconyxMakernoteDirectory.TagSequence, new[] { reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagSequence), reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagSequence + 2) }); uint eventNumberHigh = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagEventNumber); uint eventNumberLow = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagEventNumber + 2); directory.Set(ReconyxMakernoteDirectory.TagEventNumber, (eventNumberHigh << 16) + eventNumberLow); ushort seconds = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal); ushort minutes = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal + 2); ushort hour = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal + 4); ushort month = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal + 6); ushort day = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal + 8); ushort year = reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagDateTimeOriginal + 10); directory.Set(ReconyxMakernoteDirectory.TagDateTimeOriginal, new DateTime(year, month, day, hour, minutes, seconds)); directory.Set(ReconyxMakernoteDirectory.TagMoonPhase, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagMoonPhase)); directory.Set(ReconyxMakernoteDirectory.TagAmbientTemperatureFarenheit, reader.GetInt16(makernoteOffset + ReconyxMakernoteDirectory.TagAmbientTemperatureFarenheit)); directory.Set(ReconyxMakernoteDirectory.TagAmbientTemperature, reader.GetInt16(makernoteOffset + ReconyxMakernoteDirectory.TagAmbientTemperature)); directory.Set(ReconyxMakernoteDirectory.TagSerialNumber, reader.GetString(makernoteOffset + ReconyxMakernoteDirectory.TagSerialNumber, 28, Encoding.Unicode)); // two unread bytes: the serial number's terminating null directory.Set(ReconyxMakernoteDirectory.TagContrast, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagContrast)); directory.Set(ReconyxMakernoteDirectory.TagBrightness, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagBrightness)); directory.Set(ReconyxMakernoteDirectory.TagSharpness, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagSharpness)); directory.Set(ReconyxMakernoteDirectory.TagSaturation, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagSaturation)); directory.Set(ReconyxMakernoteDirectory.TagInfraredIlluminator, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagInfraredIlluminator)); directory.Set(ReconyxMakernoteDirectory.TagMotionSensitivity, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagMotionSensitivity)); directory.Set(ReconyxMakernoteDirectory.TagBatteryVoltage, reader.GetUInt16(makernoteOffset + ReconyxMakernoteDirectory.TagBatteryVoltage) / 1000.0); directory.Set(ReconyxMakernoteDirectory.TagUserLabel, reader.GetNullTerminatedString(makernoteOffset + ReconyxMakernoteDirectory.TagUserLabel, 44)); }
private static void ProcessBinary([NotNull] Directory directory, int tagValueOffset, [NotNull] IndexedReader reader, int byteCount, bool issigned = true, int arrayLength = 1) { // expects signed/unsigned int16 (for now) int byteSize = issigned ? sizeof(short) : sizeof(ushort); // 'directory' is assumed to contain tags that correspond to the byte position unless it's a set of bytes for (var i = 0; i < byteCount; i++) { if (directory.HasTagName(i)) { // only process this tag if the 'next' integral tag exists. Otherwise, it's a set of bytes if (i < byteCount - 1 && directory.HasTagName(i + 1)) { if (issigned) { directory.Set(i, reader.GetInt16(tagValueOffset + (i * byteSize))); } else { directory.Set(i, reader.GetUInt16(tagValueOffset + (i * byteSize))); } } else { // the next arrayLength bytes are a multi-byte value if (issigned) { short[] val = new short[arrayLength]; for (int j = 0; j < val.Length; j++) { val[j] = reader.GetInt16(tagValueOffset + ((i + j) * byteSize)); } directory.Set(i, val); } else { ushort[] val = new ushort[arrayLength]; for (int j = 0; j < val.Length; j++) { val[j] = reader.GetUInt16(tagValueOffset + ((i + j) * byteSize)); } directory.Set(i, val); } i += arrayLength - 1; } } } }
/// <summary>Processes a TIFF data sequence.</summary> /// <param name="reader">the <see cref="IndexedReader"/> from which the data should be read</param> /// <param name="handler">the <see cref="ITiffHandler"/> that will coordinate processing and accept read values</param> /// <exception cref="TiffProcessingException">if an error occurred during the processing of TIFF data that could not be ignored or recovered from</exception> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> /// <exception cref="TiffProcessingException"/> public static void ProcessTiff(IndexedReader reader, ITiffHandler handler) { // Read byte order. var byteOrder = reader.GetInt16(0); reader = byteOrder switch { 0x4d4d => reader.WithByteOrder(isMotorolaByteOrder: true), 0x4949 => reader.WithByteOrder(isMotorolaByteOrder: false), _ => throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + reader.GetInt16(0)), }; // Check the next two values for correctness. int tiffMarker = reader.GetUInt16(2); handler.SetTiffMarker(tiffMarker); var firstIfdOffset = reader.GetInt32(4); // David Ekholm sent a digital camera image that has this problem // TODO calling Length should be avoided as it causes IndexedCapturingReader to read to the end of the stream if (firstIfdOffset >= reader.Length - 1) { handler.Warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset"); // First directory normally starts immediately after the offset bytes, so try that firstIfdOffset = 2 + 2 + 4; } var processedIfdOffsets = new HashSet <int>(); ProcessIfd(handler, reader, processedIfdOffsets, firstIfdOffset); }
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); } }
/// <summary>Processes a TIFF data sequence.</summary> /// <param name="reader">the <see cref="IndexedReader"/> from which the data should be read</param> /// <param name="handler">the <see cref="ITiffHandler"/> that will coordinate processing and accept read values</param> /// <param name="tiffHeaderOffset">the offset within <c>reader</c> at which the TIFF header starts</param> /// <exception cref="TiffProcessingException">if an error occurred during the processing of TIFF data that could not be ignored or recovered from</exception> /// <exception cref="System.IO.IOException">an error occurred while accessing the required data</exception> /// <exception cref="TiffProcessingException"/> public static void ProcessTiff([NotNull] IndexedReader reader, [NotNull] ITiffHandler handler, int tiffHeaderOffset = 0) { // Read byte order. switch (reader.GetInt16(tiffHeaderOffset)) { case 0x4d4d: // MM reader.IsMotorolaByteOrder = true; break; case 0x4949: // II reader.IsMotorolaByteOrder = false; break; default: throw new TiffProcessingException("Unclear distinction between Motorola/Intel byte ordering: " + reader.GetInt16(tiffHeaderOffset)); } // Check the next two values for correctness. int tiffMarker = reader.GetUInt16(2 + tiffHeaderOffset); handler.SetTiffMarker(tiffMarker); var firstIfdOffset = reader.GetInt32(4 + tiffHeaderOffset) + tiffHeaderOffset; // David Ekholm sent a digital camera image that has this problem // TODO calling Length should be avoided as it causes IndexedCapturingReader to read to the end of the stream if (firstIfdOffset >= reader.Length - 1) { handler.Warn("First IFD offset is beyond the end of the TIFF data segment -- trying default offset"); // First directory normally starts immediately after the offset bytes, so try that firstIfdOffset = tiffHeaderOffset + 2 + 2 + 4; } var processedIfdOffsets = new HashSet <int>(); ProcessIfd(handler, reader, processedIfdOffsets, firstIfdOffset, tiffHeaderOffset); handler.Completed(reader, tiffHeaderOffset); }
/// <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; } } }