/// <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);
        }
Exemple #2
0
        /// <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);
        }
Exemple #3
0
        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);
            }
        }
Exemple #4
0
        /// <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);
        }
Exemple #5
0
        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);
                }
            }
Exemple #6
0
        /// <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);
        }