/// <summary>Reads JFIF values and returns them in an <see cref="JfifDirectory"/>.</summary> public JfifDirectory Extract(IndexedReader reader) { var directory = new JfifDirectory(); try { // For JFIF the tag number is the value's byte offset directory.Set(JfifDirectory.TagVersion, reader.GetUInt16(JfifDirectory.TagVersion)); directory.Set(JfifDirectory.TagUnits, reader.GetByte(JfifDirectory.TagUnits)); directory.Set(JfifDirectory.TagResX, reader.GetUInt16(JfifDirectory.TagResX)); directory.Set(JfifDirectory.TagResY, reader.GetUInt16(JfifDirectory.TagResY)); directory.Set(JfifDirectory.TagThumbWidth, reader.GetByte(JfifDirectory.TagThumbWidth)); directory.Set(JfifDirectory.TagThumbHeight, reader.GetByte(JfifDirectory.TagThumbHeight)); } catch (IOException e) { directory.AddError(e.Message); } return(directory); }
/// <summary>Reads JFXX values and returns them in an <see cref="JfxxDirectory"/>.</summary> public JfxxDirectory Extract(IndexedReader reader) { var directory = new JfxxDirectory(); try { // For JFIF the tag number is the value's byte offset directory.Set(JfxxDirectory.TagExtensionCode, reader.GetByte(JfxxDirectory.TagExtensionCode)); } catch (IOException e) { directory.AddError(e.Message); } return(directory); }
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 bool ProcessMakernote(int makernoteOffset, [NotNull] ICollection <int> processedIfdOffsets, int tiffHeaderOffset, [NotNull] IndexedReader reader) { // Determine the camera model and makernote format. var ifd0Directory = Directories.OfType <ExifIfd0Directory>().FirstOrDefault(); if (ifd0Directory == null) { return(false); } var cameraMake = ifd0Directory.GetString(ExifDirectoryBase.TagMake); var firstTwoChars = reader.GetString(makernoteOffset, 2); var firstThreeChars = reader.GetString(makernoteOffset, 3); var firstFourChars = reader.GetString(makernoteOffset, 4); var firstFiveChars = reader.GetString(makernoteOffset, 5); var firstSixChars = reader.GetString(makernoteOffset, 6); var firstSevenChars = reader.GetString(makernoteOffset, 7); var firstEightChars = reader.GetString(makernoteOffset, 8); var firstTwelveChars = reader.GetString(makernoteOffset, 12); var byteOrderBefore = reader.IsMotorolaByteOrder; if ("OLYMP" == firstFiveChars || "EPSON" == firstFiveChars || "AGFA" == firstFourChars) { // Olympus Makernote // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ PushDirectory(typeof(OlympusMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset); } else if (cameraMake != null && cameraMake.ToUpper().StartsWith("MINOLTA")) { // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote // area that commences immediately. PushDirectory(typeof(OlympusMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset); } else if (cameraMake != null && cameraMake.Trim().ToUpper().StartsWith("NIKON")) { if ("Nikon" == firstFiveChars) { switch (reader.GetByte(makernoteOffset + 6)) { case 1: { /* There are two scenarios here: * Type 1: ** * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ * Type 3: ** * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 */ PushDirectory(typeof(NikonType1MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset); break; } case 2: { PushDirectory(typeof(NikonType2MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 18, makernoteOffset + 10); break; } default: { ifd0Directory.AddError("Unsupported Nikon makernote data ignored."); break; } } } else { // The IFD begins with the first Makernote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models. PushDirectory(typeof(NikonType2MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset); } } else if ("SONY CAM" == firstEightChars || "SONY DSC" == firstEightChars) { PushDirectory(typeof(SonyType1MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset); } else if ("SEMC MS\u0000\u0000\u0000\u0000\u0000" == firstTwelveChars) { // force MM for this directory reader.IsMotorolaByteOrder = true; // skip 12 byte header + 2 for "MM" + 6 PushDirectory(typeof(SonyType6MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 20, tiffHeaderOffset); } else if ("SIGMA\u0000\u0000\u0000" == firstEightChars || "FOVEON\u0000\u0000" == firstEightChars) { PushDirectory(typeof(SigmaMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 10, tiffHeaderOffset); } else if ("KDK" == firstThreeChars) { reader.IsMotorolaByteOrder = firstSevenChars == "KDK INFO"; var directory = new KodakMakernoteDirectory(); Directories.Add(directory); ProcessKodakMakernote(directory, makernoteOffset, reader); } else if ("Canon".Equals(cameraMake, StringComparison.OrdinalIgnoreCase)) { PushDirectory(typeof(CanonMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset); } else if (cameraMake != null && cameraMake.ToUpper().StartsWith("CASIO")) { if ("QVC\u0000\u0000\u0000" == firstSixChars) { PushDirectory(typeof(CasioType2MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 6, tiffHeaderOffset); } else { PushDirectory(typeof(CasioType1MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset, tiffHeaderOffset); } } else if ("FUJIFILM" == firstEightChars || "Fujifilm".Equals(cameraMake, StringComparison.OrdinalIgnoreCase)) { // Note that this also applies to certain Leica cameras, such as the Digilux-4.3 reader.IsMotorolaByteOrder = false; // the 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote // IFD, though the offset is relative to the start of the makernote, not the TIFF // header (like everywhere else) var ifdStart = makernoteOffset + reader.GetInt32(makernoteOffset + 8); PushDirectory(typeof(FujifilmMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, ifdStart, makernoteOffset); } else if ("KYOCERA" == firstSevenChars) { // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html PushDirectory(typeof(KyoceraMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 22, tiffHeaderOffset); } else if ("LEICA" == firstFiveChars) { reader.IsMotorolaByteOrder = false; if ("Leica Camera AG" == cameraMake) { PushDirectory(typeof(LeicaMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset); } else if ("LEICA" == cameraMake) { // Some Leica cameras use Panasonic makernote tags PushDirectory(typeof(PanasonicMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, tiffHeaderOffset); } else { return(false); } } else if ("Panasonic\u0000\u0000\u0000" == reader.GetString(makernoteOffset, 12)) { // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html PushDirectory(typeof(PanasonicMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 12, tiffHeaderOffset); } else if ("AOC\u0000" == firstFourChars) { // NON-Standard TIFF IFD Data using Casio Type 2 Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - Pentax ist D PushDirectory(typeof(CasioType2MakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 6, makernoteOffset); } else if (cameraMake != null && (cameraMake.ToUpper().StartsWith("PENTAX") || cameraMake.ToUpper().StartsWith("ASAHI"))) { // NON-Standard TIFF IFD Data using Pentax Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - PENTAX Optio 330 // - PENTAX Optio 430 PushDirectory(typeof(PentaxMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset, makernoteOffset); } // else if ("KC" == firstTwoChars || "MINOL" == firstFiveChars || "MLY" == firstThreeChars || "+M+M+M+M" == firstEightChars) // { // // This Konica data is not understood. Header identified in accordance with information at this site: // // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html // // TODO add support for minolta/konica cameras // exifDirectory.addError("Unsupported Konica/Minolta data ignored."); // } else if ("SANYO\x0\x1\x0" == firstEightChars) { PushDirectory(typeof(SanyoMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, makernoteOffset); } else if (cameraMake != null && cameraMake.ToLower().StartsWith("ricoh")) { if (firstTwoChars == "Rv" || firstThreeChars == "Rev") { // This is a textual format, where the makernote bytes look like: // Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2E00:������������������������������ // Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2D05:������������������������������ // Rv0207;Sf6C84;Rg76;Bg60;Gg42;Ll0;Ld0;Aj0004;Bn0B02900;Fp10B8;Md6700;Ln116900086D27;Sv263:0000000000000000000000�� // This format is currently unsupported return(false); } if (firstFiveChars.Equals("Ricoh", StringComparison.OrdinalIgnoreCase)) { // Always in Motorola byte order reader.IsMotorolaByteOrder = true; PushDirectory(typeof(RicohMakernoteDirectory)); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8, makernoteOffset); } } else { // The makernote is not comprehended by this library. // If you are reading this and believe a particular camera's image should be processed, get in touch. return(false); } reader.IsMotorolaByteOrder = byteOrderBefore; return(true); }
private static bool PopulateHeader(IndexedReader reader, TgaHeaderDirectory directory, out int idLength, out ColormapInfo colormap) { idLength = reader.GetByte(0); directory.Set(TgaHeaderDirectory.TagIdLength, idLength); SetDataType(); colormap = SetColormapInfo(); SetGeometry(); SetDepth(); SetFlags(); return(true); byte SetDataType() { var dataType = reader.GetByte(2); return(dataType switch { 1 => SetDataTypeMapped(), 9 => SetDataTypeMapped(), 2 => SetDataTypeTrueColor(), 10 => SetDataTypeTrueColor(), 3 => SetDataTypeGrayscale(), 11 => SetDataTypeGrayscale(), _ => throw new ImageProcessingException("Invalid TGA data type"), }); byte SetDataTypeMapped() { var colormapType = reader.GetByte(1); if (colormapType == 0) { throw new ImageProcessingException("Invalid TGA data type / color map type combo"); } directory.Set(TgaHeaderDirectory.TagDataType, dataType); return(dataType); } byte SetDataTypeTrueColor() { var colormapType = reader.GetByte(1); if (colormapType != 0) { throw new ImageProcessingException("Invalid TGA data type / color map type combo"); } directory.Set(TgaHeaderDirectory.TagDataType, dataType); return(dataType); } byte SetDataTypeGrayscale() { var colormapType = reader.GetByte(1); if (colormapType != 0) { throw new ImageProcessingException("Invalid TGA data type / color map type combo"); } directory.Set(TgaHeaderDirectory.TagDataType, dataType); return(dataType); } }
/// <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; } } }
/// <exception cref="System.IO.IOException"/> private bool ProcessMakernote(int makernoteOffset, [NotNull] ICollection <int> processedIfdOffsets, [NotNull] IndexedReader reader) { Debug.Assert(makernoteOffset >= 0, "makernoteOffset >= 0"); var cameraMake = Directories.OfType <ExifIfd0Directory>().FirstOrDefault()?.GetString(ExifDirectoryBase.TagMake); var firstTwoChars = reader.GetString(makernoteOffset, 2, Encoding.UTF8); var firstThreeChars = reader.GetString(makernoteOffset, 3, Encoding.UTF8); var firstFourChars = reader.GetString(makernoteOffset, 4, Encoding.UTF8); var firstFiveChars = reader.GetString(makernoteOffset, 5, Encoding.UTF8); var firstSixChars = reader.GetString(makernoteOffset, 6, Encoding.UTF8); var firstSevenChars = reader.GetString(makernoteOffset, 7, Encoding.UTF8); var firstEightChars = reader.GetString(makernoteOffset, 8, Encoding.UTF8); var firstTenChars = reader.GetString(makernoteOffset, 10, Encoding.UTF8); var firstTwelveChars = reader.GetString(makernoteOffset, 12, Encoding.UTF8); if (string.Equals("OLYMP\0", firstSixChars, StringComparison.Ordinal) || string.Equals("EPSON", firstFiveChars, StringComparison.Ordinal) || string.Equals("AGFA", firstFourChars, StringComparison.Ordinal)) { // Olympus Makernote // Epson and Agfa use Olympus makernote standard: http://www.ozhiker.com/electronics/pjmt/jpeg_info/ PushDirectory(new OlympusMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8); } else if (string.Equals("OLYMPUS\0II", firstTenChars, StringComparison.Ordinal)) { // Olympus Makernote (alternate) // Note that data is relative to the beginning of the makernote // http://exiv2.org/makernote.html PushDirectory(new OlympusMakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 12); } else if (cameraMake != null && cameraMake.StartsWith("MINOLTA", StringComparison.OrdinalIgnoreCase)) { // Cases seen with the model starting with MINOLTA in capitals seem to have a valid Olympus makernote // area that commences immediately. PushDirectory(new OlympusMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } else if (cameraMake != null && cameraMake.TrimStart().StartsWith("NIKON", StringComparison.OrdinalIgnoreCase)) { if (string.Equals("Nikon", firstFiveChars, StringComparison.Ordinal)) { switch (reader.GetByte(makernoteOffset + 6)) { case 1: { /* There are two scenarios here: * Type 1: ** * :0000: 4E 69 6B 6F 6E 00 01 00-05 00 02 00 02 00 06 00 Nikon........... * :0010: 00 00 EC 02 00 00 03 00-03 00 01 00 00 00 06 00 ................ * Type 3: ** * :0000: 4E 69 6B 6F 6E 00 02 00-00 00 4D 4D 00 2A 00 00 Nikon....MM.*... * :0010: 00 08 00 1E 00 01 00 07-00 00 00 04 30 32 30 30 ............0200 */ PushDirectory(new NikonType1MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 8); break; } case 2: { PushDirectory(new NikonType2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset + 10), processedIfdOffsets, 8); break; } default: { Error("Unsupported Nikon makernote data ignored."); break; } } } else { // The IFD begins with the first Makernote byte (no ASCII name). This occurs with CoolPix 775, E990 and D1 models. PushDirectory(new NikonType2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } } else if (string.Equals("SONY CAM", firstEightChars, StringComparison.Ordinal) || string.Equals("SONY DSC", firstEightChars, StringComparison.Ordinal)) { PushDirectory(new SonyType1MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 12); } // Do this check LAST after most other Sony checks else if (cameraMake != null && cameraMake.StartsWith("SONY", StringComparison.Ordinal) && reader.GetBytes(makernoteOffset, 2) != new byte[] { 0x01, 0x00 }) { // The IFD begins with the first Makernote byte (no ASCII name). Used in SR2 and ARW images PushDirectory(new SonyType1MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } else if (string.Equals("SEMC MS\u0000\u0000\u0000\u0000\u0000", firstTwelveChars, StringComparison.Ordinal)) { // Force Motorola byte order for this directory // skip 12 byte header + 2 for "MM" + 6 PushDirectory(new SonyType6MakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithByteOrder(isMotorolaByteOrder: true), processedIfdOffsets, makernoteOffset + 20); } else if (string.Equals("SIGMA\u0000\u0000\u0000", firstEightChars, StringComparison.Ordinal) || string.Equals("FOVEON\u0000\u0000", firstEightChars, StringComparison.Ordinal)) { PushDirectory(new SigmaMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 10); } else if (string.Equals("KDK", firstThreeChars, StringComparison.Ordinal)) { var directory = new KodakMakernoteDirectory(); Directories.Add(directory); ProcessKodakMakernote(directory, makernoteOffset, reader.WithByteOrder(isMotorolaByteOrder: firstSevenChars == "KDK INFO")); } else if ("CANON".Equals(cameraMake, StringComparison.OrdinalIgnoreCase)) { PushDirectory(new CanonMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } else if (cameraMake != null && cameraMake.StartsWith("CASIO", StringComparison.OrdinalIgnoreCase)) { if (string.Equals("QVC\u0000\u0000\u0000", firstSixChars, StringComparison.Ordinal)) { PushDirectory(new CasioType2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 6); } else { PushDirectory(new CasioType1MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } } else if (string.Equals("FUJIFILM", firstEightChars, StringComparison.Ordinal) || string.Equals("FUJIFILM", cameraMake, StringComparison.OrdinalIgnoreCase)) { // Note that this also applies to certain Leica cameras, such as the Digilux-4.3. // The 4 bytes after "FUJIFILM" in the makernote point to the start of the makernote // IFD, though the offset is relative to the start of the makernote, not the TIFF header var makernoteReader = reader.WithShiftedBaseOffset(makernoteOffset).WithByteOrder(isMotorolaByteOrder: false); var ifdStart = makernoteReader.GetInt32(8); PushDirectory(new FujifilmMakernoteDirectory()); TiffReader.ProcessIfd(this, makernoteReader, processedIfdOffsets, ifdStart); } else if (string.Equals("KYOCERA", firstSevenChars, StringComparison.Ordinal)) { // http://www.ozhiker.com/electronics/pjmt/jpeg_info/kyocera_mn.html PushDirectory(new KyoceraMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 22); } else if (string.Equals("LEICA", firstFiveChars, StringComparison.Ordinal)) { // used by the X1/X2/X VARIO/T // (X1 starts with "LEICA\0\x01\0", Make is "LEICA CAMERA AG") // (X2 starts with "LEICA\0\x05\0", Make is "LEICA CAMERA AG") // (X VARIO starts with "LEICA\0\x04\0", Make is "LEICA CAMERA AG") // (T (Typ 701) starts with "LEICA\0\0x6", Make is "LEICA CAMERA AG") // (X (Typ 113) starts with "LEICA\0\0x7", Make is "LEICA CAMERA AG") if (string.Equals("LEICA\0\x1\0", firstEightChars, StringComparison.Ordinal) || string.Equals("LEICA\0\x4\0", firstEightChars, StringComparison.Ordinal) || string.Equals("LEICA\0\x5\0", firstEightChars, StringComparison.Ordinal) || string.Equals("LEICA\0\x6\0", firstEightChars, StringComparison.Ordinal) || string.Equals("LEICA\0\x7\0", firstEightChars, StringComparison.Ordinal)) { PushDirectory(new LeicaType5MakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 8); } else if (string.Equals("Leica Camera AG", cameraMake, StringComparison.Ordinal)) { PushDirectory(new LeicaMakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithByteOrder(isMotorolaByteOrder: false), processedIfdOffsets, makernoteOffset + 8); } else if (string.Equals("LEICA", cameraMake, StringComparison.Ordinal)) { // Some Leica cameras use Panasonic makernote tags PushDirectory(new PanasonicMakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithByteOrder(isMotorolaByteOrder: false), processedIfdOffsets, makernoteOffset + 8); } else { return(false); } } else if (string.Equals("Panasonic\u0000\u0000\u0000", firstTwelveChars, StringComparison.Ordinal)) { // NON-Standard TIFF IFD Data using Panasonic Tags. There is no Next-IFD pointer after the IFD // Offsets are relative to the start of the TIFF header at the beginning of the EXIF segment // more information here: http://www.ozhiker.com/electronics/pjmt/jpeg_info/panasonic_mn.html PushDirectory(new PanasonicMakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset + 12); } else if (string.Equals("AOC\u0000", firstFourChars, StringComparison.Ordinal)) { // NON-Standard TIFF IFD Data using Casio Type 2 Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - Pentax ist D PushDirectory(new CasioType2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 6); } else if (cameraMake != null && (cameraMake.StartsWith("PENTAX", StringComparison.OrdinalIgnoreCase) || cameraMake.StartsWith("ASAHI", StringComparison.OrdinalIgnoreCase))) { // NON-Standard TIFF IFD Data using Pentax Tags // IFD has no Next-IFD pointer at end of IFD, and // Offsets are relative to the start of the current IFD tag, not the TIFF header // Observed for: // - PENTAX Optio 330 // - PENTAX Optio 430 PushDirectory(new PentaxMakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 0); } // else if ("KC" == firstTwoChars || "MINOL" == firstFiveChars || "MLY" == firstThreeChars || "+M+M+M+M" == firstEightChars) // { // // This Konica data is not understood. Header identified in accordance with information at this site: // // http://www.ozhiker.com/electronics/pjmt/jpeg_info/minolta_mn.html // // TODO add support for minolta/konica cameras // exifDirectory.addError("Unsupported Konica/Minolta data ignored."); // } else if (string.Equals("SANYO\x0\x1\x0", firstEightChars, StringComparison.Ordinal)) { PushDirectory(new SanyoMakernoteDirectory()); TiffReader.ProcessIfd(this, reader.WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 8); } else if (cameraMake != null && cameraMake.StartsWith("RICOH", StringComparison.OrdinalIgnoreCase)) { if (string.Equals(firstTwoChars, "Rv", StringComparison.Ordinal) || string.Equals(firstThreeChars, "Rev", StringComparison.Ordinal)) { // This is a textual format, where the makernote bytes look like: // Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2E00:������������������������������ // Rv0103;Rg1C;Bg18;Ll0;Ld0;Aj0000;Bn0473800;Fp2D05:������������������������������ // Rv0207;Sf6C84;Rg76;Bg60;Gg42;Ll0;Ld0;Aj0004;Bn0B02900;Fp10B8;Md6700;Ln116900086D27;Sv263:0000000000000000000000�� // This format is currently unsupported return(false); } if (firstFiveChars.Equals("RICOH", StringComparison.OrdinalIgnoreCase)) { PushDirectory(new RicohMakernoteDirectory()); // Always in Motorola byte order TiffReader.ProcessIfd(this, reader.WithByteOrder(isMotorolaByteOrder: true).WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 8); } } else if (string.Equals(firstTenChars, "Apple iOS\0", StringComparison.Ordinal)) { PushDirectory(new AppleMakernoteDirectory()); // Always in Motorola byte order TiffReader.ProcessIfd(this, reader.WithByteOrder(isMotorolaByteOrder: true).WithShiftedBaseOffset(makernoteOffset), processedIfdOffsets, 14); } else if (string.Equals("RECONYX", cameraMake, StringComparison.OrdinalIgnoreCase) || reader.GetUInt16(makernoteOffset) == ReconyxMakernoteDirectory.HyperFireMakernoteVersion) { var directory = new ReconyxMakernoteDirectory(); Directories.Add(directory); ProcessReconyxMakernote(directory, makernoteOffset, reader); } else if (string.Equals("SAMSUNG", cameraMake, StringComparison.Ordinal)) { // Only handles Type2 notes correctly. Others aren't implemented, and it's complex to determine which ones to use PushDirectory(new SamsungType2MakernoteDirectory()); TiffReader.ProcessIfd(this, reader, processedIfdOffsets, makernoteOffset); } else { // The makernote is not comprehended by this library. // If you are reading this and believe a particular camera's image should be processed, get in touch. return(false); } return(true); }