Exemple #1
0
        public override void Parse(Stream file)
        {
            base.Parse(file);

            Tag subifdoffsetTag;

            if (!ifd.tags.TryGetValue(0x14A, out subifdoffsetTag))
            {
                throw new FormatException("File not correct");
            }
            subifd = new IFD[subifdoffsetTag.dataCount];
            for (int i = 0; i < subifdoffsetTag.dataCount; i++)
            {
                subifd[i] = new IFD(fileStream, (uint)subifdoffsetTag.data[i], true, false);
            }
            //get the Exif
            Tag exifoffsetTag;

            if (!ifd.tags.TryGetValue(0x8769, out exifoffsetTag))
            {
                throw new FormatException("File not correct");
            }
            //todo third IFD
            exif = new IFD(fileStream, (uint)exifoffsetTag.data[0], true, false);
            Tag makerNoteOffsetTag;

            if (!exif.tags.TryGetValue(0x927C, out makerNoteOffsetTag))
            {
                throw new FormatException("File not correct");
            }
            makerNote = new NikonMakerNote(fileStream, makerNoteOffsetTag.dataOffset, true);
        }
Exemple #2
0
            /// <summary>
            /// Extracts the XMP packet.
            /// </summary>
            /// <param name="reader">The reader.</param>
            /// <returns>The extracted XMP packet.</returns>
            /// <exception cref="EndOfStreamException">The end of the stream has been reached.</exception>
            private static byte[] ExtractXMPPacket(EndianBinaryReader reader)
            {
                ushort signature = reader.ReadUInt16();

                if (signature == TIFFSignature)
                {
                    uint ifdOffset = reader.ReadUInt32();
                    reader.Position = ifdOffset;

                    int ifdCount = reader.ReadUInt16();

                    for (int i = 0; i < ifdCount; i++)
                    {
                        IFD ifd = new IFD(reader);

                        if (ifd.tag == XmpTag && (ifd.type == DataType.Byte || ifd.type == DataType.Undefined))
                        {
                            if (ifd.count > int.MaxValue)
                            {
                                // The .NET Framework does not support arrays larger than 2 GB.
                                return(null);
                            }

                            reader.Position = ifd.offset;

                            return(reader.ReadBytes((int)ifd.count));
                        }
                    }
                }

                return(null);
            }
Exemple #3
0
            public DE(byte[] de, FileStream fs, IFD fd)
            {
                Tag = (ushort)fd.Head.getValue(de.Take(2), 3); //BitConverter.ToUInt16(ifh.Bit == "MM" ? .Reverse().ToArray() : de.Take(2).ToArray(), 0);
                if (Tag != 256 && Tag != 257 && Tag != 273 && Tag != 279)
                {
                    return;
                }
                Type   = (ushort)fd.Head.getValue(de.Skip(2).Take(2), 3); //BitConverter.ToUInt16(ifh.Bit == "MM" ? de.Skip(2).Take(2).Reverse().ToArray() : de.Skip(2).Take(2).ToArray(), 0);
                Length = (uint)fd.Head.getValue(de.Skip(4).Take(4), 4);   // BitConverter.ToUInt32(ifh.Bit == "MM" ? de.Skip(4).Take(4).Reverse().ToArray() : de.Skip(4).Take(4).ToArray(), 0);
                var v = Convert.ToInt64(fd.Head.getValue(de.Skip(8).Take(4), Type));

                if (Length * Type > 4)
                {
                    try
                    {
                        fs.Seek(v, SeekOrigin.Begin);
                        var dt = new byte[Length * (ulong)tps[Type]];
                        fs.Read(dt, 0, dt.Length);
                        Value = dt;
                    }
                    catch { }
                }
                else
                {
                    Value = v;
                }
            }
Exemple #4
0
        public NikonMakerNote(TIFFBinaryReader buffer, uint offset, bool compression)
        {
            //read the header
            buffer.BaseStream.Position = offset;
            stringMagic = "";
            this.offset = offset;
            for (int i = 0; i < 6; i++)
            {
                stringMagic += buffer.ReadChar();
            }

            version = buffer.ReadUInt16();
            buffer.BaseStream.Position = 2 + offset; //jump the padding

            header = new Header(buffer, 0);          //0 car beggining of the stream

            if (header.byteOrder == 0x4D4D)
            {
                buffer = new TIFFBinaryReaderRE(buffer.BaseStream);
                //TODO see if need to move
            }
            ifd = new IFD(buffer, header.TIFFoffset + getOffset(), true, true);
            //ifd = new IFD(buffer, (uint)buffer.BaseStream.Position, true, true);
            Tag previewOffsetTag;

            if (ifd.tags.TryGetValue(17, out previewOffsetTag))
            {
                preview = new IFD(buffer, (uint)previewOffsetTag.data[0] + getOffset(), true, false);
            }
            else
            {
                preview = null;  //no preview in this file
            }
        }
Exemple #5
0
        /* Parse FUJI information */
        /* It is a simpler form of Tiff IFD, so we add them as TiffEntries */
        void ParseFuji(uint offset)
        {
            try
            {
                IFD tempIFD = new IFD(ifd.endian, ifd.Depth);
                ImageBinaryReaderBigEndian bytes = new ImageBinaryReaderBigEndian(stream, offset);
                uint entries = bytes.ReadUInt32();

                if (entries > 255)
                {
                    throw new RawDecoderException("Too many entries");
                }

                for (int i = 0; i < entries; i++)
                {
                    UInt16 tag    = bytes.ReadUInt16();
                    uint   length = bytes.ReadUInt16();
                    Tag    t;

                    // Set types of known tags
                    switch (tag)
                    {
                    case 0x100:
                    case 0x121:
                    case 0x2ff0:
                        t = new Tag((TagType)tag, TiffDataType.SHORT, length / 2);
                        for (int k = 0; k < t.dataCount; k++)
                        {
                            t.data[k] = bytes.ReadUInt16();
                        }
                        break;

                    case 0xc000:
                        // This entry seem to have swapped endianness:
                        t = new Tag((TagType)tag, TiffDataType.LONG, length / 4);
                        for (int k = 0; k < t.dataCount; k++)
                        {
                            t.data[k] = bytes.ReadUInt32();
                        }
                        break;

                    default:
                        t = new Tag((TagType)tag, TiffDataType.UNDEFINED, length);
                        for (int k = 0; k < t.dataCount; k++)
                        {
                            t.data[k] = bytes.ReadByte();
                        }
                        break;
                    }
                    tempIFD.tags.Add(t.TagId, t);
                    //bytes.ReadBytes((int)length);
                }
                ifd.subIFD.Add(tempIFD);
            }
            catch (IOException)
            {
                throw new RawDecoderException("IO error occurred during parsing. Skipping the rest");
            }
        }
Exemple #6
0
        protected void Parse(uint offset)
        {
            //parse the ifd
            if (stream.Length < 16)
            {
                throw new RawDecoderException("Not a TIFF file (size too small)");
            }
            Endianness endian = Endianness.Little;

            byte[] data = new byte[5];
            stream.Position = offset;
            stream.Read(data, 0, 4);
            if (data[0] == 0x4D || data[1] == 0x4D)
            {
                //open binaryreader
                reader = new ImageBinaryReaderBigEndian(stream);
                endian = Endianness.Big;
                if (data[3] != 42 && data[3] != 0x4f) // ORF sometimes has 0x4f!
                {
                    throw new RawDecoderException("Not a TIFF file (magic 42)");
                }
            }
            else if (data[0] == 0x49 || data[1] == 0x49)
            {
                reader = new ImageBinaryReader(stream);
                if (data[2] != 42 && data[2] != 0x52 && data[2] != 0x55) // ORF has 0x52, RW2 0x55!
                {
                    throw new RawDecoderException("Not a TIFF file (magic 42)");
                }
            }
            else
            {
                throw new RawDecoderException("Not a TIFF file (ID)");
            }
            reader.Position = offset + 4;

            var newIfd = new IFD(reader, reader.ReadUInt32(), endian, 0, (int)offset);

            if (ifd == null)
            {
                ifd = newIfd;
            }
            else
            {
                ifd.subIFD.Add(newIfd);
            }
            uint nextIFD = newIfd.NextOffset;

            while (nextIFD != 0)
            {
                ifd.subIFD.Add(new IFD(reader, nextIFD, endian, 0, (int)offset));
                if (ifd.subIFD.Count > 100)
                {
                    throw new RawDecoderException("TIFF file has too many Sub IFDs, probably broken");
                }
                nextIFD = (ifd.subIFD[ifd.subIFD.Count - 1]).NextOffset;
            }
        }
        /// <summary>
        /// Removes all items with the given IFD from the collection.
        /// </summary>
        /// <param name="ifd">The IFD section to remove.</param>
        public void Remove(IFD ifd)
        {
            List <T> toRemove = new List <T>();

            foreach (T item in items)
            {
                if (item.IFD == ifd)
                {
                    toRemove.Add(item);
                }
            }
            Remove(toRemove);
        }
Exemple #8
0
        private void ParseData(byte[] data)
        {
            EndianFlag = Encoding.GetEncoding("ISO-8859-1").GetString(data.Take(2).ToArray());
            int index = 2;

            if (Converter.ToInt16(data, index) != 42)
            {
                throw new FormatException("eXIf data is not in TIFF format");
            }
            index += 2;
            Offset = Converter.ToInt32(data, index);
            Ifd    = new IFD(data, Offset);
        }
Exemple #9
0
            /// <summary>
            /// Extracts the XMP packet from a TIFF file.
            /// </summary>
            /// <param name="stream">The stream to read.</param>
            /// <returns>The extracted XMP packet, or null.</returns>
            internal static byte[] ExtractXMP(Stream stream)
            {
                stream.Position = 0L;

                try
                {
                    ushort byteOrder = ReadShort(stream, false);

                    bool littleEndian = byteOrder == LittleEndianByteOrder;

                    ushort signature = ReadShort(stream, littleEndian);

                    if (signature == TIFFSignature)
                    {
                        uint ifdOffset = ReadLong(stream, littleEndian);
                        stream.Seek(ifdOffset, SeekOrigin.Begin);

                        int ifdCount = ReadShort(stream, littleEndian);

                        for (int i = 0; i < ifdCount; i++)
                        {
                            IFD ifd = new IFD(stream, littleEndian);

                            if (ifd.tag == XmpTag && (ifd.type == DataType.Byte || ifd.type == DataType.Undefined))
                            {
                                stream.Seek(ifd.offset, SeekOrigin.Begin);

                                int count = (int)ifd.count;

                                byte[] xmpBytes = new byte[count];

                                int numBytesToRead = count;
                                int numBytesRead   = 0;
                                do
                                {
                                    int n = stream.Read(xmpBytes, numBytesRead, numBytesToRead);
                                    numBytesRead   += n;
                                    numBytesToRead -= n;
                                } while (numBytesToRead > 0);

                                return(xmpBytes);
                            }
                        }
                    }
                }
                catch (EndOfStreamException)
                {
                }

                return(null);
            }
Exemple #10
0
 void SetBlack(IFD raw)
 {
     if (raw.tags.ContainsKey(TagType.MASKEDAREAS))
     {
         if (DecodeMaskedAreas(raw))
         {
             return;
         }
     }
     if (raw.GetEntry(TagType.BLACKLEVEL) != null)
     {
         Decodeblacks(raw);
     }
 }
Exemple #11
0
        void DecodeUncompressed(IFD raw)
        {
            uint width  = raw.GetEntry(TagType.IMAGEWIDTH).GetUInt(0);
            uint height = raw.GetEntry(TagType.IMAGELENGTH).GetUInt(0);
            uint off    = raw.GetEntry(TagType.STRIPOFFSETS).GetUInt(0);

            rawImage.fullSize.dim = new Point2D(width, height);
            rawImage.Init(false);
            ImageBinaryReader input = new ImageBinaryReader(reader.BaseStream, off);

            /*
             *  RawDecompressor.Decode14BitRawBEunpacked(input, width, height, rawImage);
             */
            RawDecompressor.Decode16BitRawUnpacked(input, new Point2D(width, height), new Point2D(), rawImage);
        }
        /// <summary>
        /// Removes all items with the given IFD from the collection.
        /// </summary>
        /// <param name="ifd">The IFD section to remove.</param>
        public void Remove(IFD ifd)
        {
            List <ExifProperty> toRemove = new List <ExifProperty>();

            foreach (ExifProperty item in items)
            {
                if (item.IFD == ifd)
                {
                    toRemove.Add(item);
                }
            }
            foreach (ExifProperty tag in toRemove)
            {
                items.Remove(tag);
            }
        }
Exemple #13
0
        /// <summary>
        /// Removes all items with the given IFD from the collection.
        /// </summary>
        /// <param name="ifd">The IFD section to remove.</param>
        public void Remove(IFD ifd)
        {
            List <ExifTag> toRemove = new List <ExifTag> ();

            foreach (KeyValuePair <ExifTag, ExifProperty> item in items)
            {
                if (item.Value.IFD == ifd)
                {
                    toRemove.Add(item.Key);
                }
            }
            foreach (ExifTag tag in toRemove)
            {
                items.Remove(tag);
            }
        }
Exemple #14
0
        /// <summary>
        ///  Retrieves a numbered tag from a specific IFD
        /// </summary>
        /// <remarks>Useful for cases where a new or non-standard tag isn't present in the <see cref="ExifTags"/> enumeration</remarks>
        public bool GetTagValue <T>(ushort tagID, IFD ifd, out T result)
        {
            Dictionary <ushort, long> catalogue;

            switch (ifd)
            {
            case IFD.IFD0:
                catalogue = _ifd0PrimaryCatalogue;
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            return(GetTagValue(catalogue, tagID, out result));
        }
Exemple #15
0
        public override void DecodeRaw()
        {
            List <IFD> data = ifd.GetIFDsWithTag(TagType.STRIPOFFSETS);

            if (data.Count == 0)
            {
                throw new RawDecoderException("No image data found");
            }

            IFD raw = data[0];

            int compression = raw.GetEntry(TagType.COMPRESSION).GetInt(0);

            if (1 == compression || compression == 32773)
            {
                DecodeUncompressed(raw, BitOrder.Jpeg);
                return;
            }

            if (65535 != compression)
            {
                throw new RawDecoderException("Unsupported compression");
            }

            Tag offsets = raw.GetEntry(TagType.STRIPOFFSETS);
            Tag counts  = raw.GetEntry(TagType.STRIPBYTECOUNTS);

            if (offsets.dataCount != 1)
            {
                throw new RawDecoderException("Multiple Strips found: " + offsets.dataCount);
            }
            if (counts.dataCount != offsets.dataCount)
            {
                throw new RawDecoderException("Byte count number does not match strip size: count:" + counts.dataCount + ", strips:" + offsets.dataCount);
            }
            if (!reader.IsValid(offsets.GetUInt(0), counts.GetUInt(0)))
            {
                throw new RawDecoderException("Truncated file.");
            }

            rawImage.fullSize.dim = new Point2D(raw.GetEntry(TagType.IMAGEWIDTH).GetUInt(0), raw.GetEntry(TagType.IMAGELENGTH).GetUInt(0));
            rawImage.Init(false);
            PentaxDecompressor l = new PentaxDecompressor(reader, rawImage);

            l.DecodePentax(ifd, offsets.GetUInt(0), counts.GetUInt(0));
            reader.Dispose();
        }
Exemple #16
0
        public override void Parse(Stream file)
        {
            //Open a binary stream on the file
            fileStream = new TIFFBinaryReader(file);

            //read the first bit to get the endianness of the file
            if (fileStream.ReadUInt16() == 0x4D4D)
            {
                //File is in reverse bit order
                // fileStream.Dispose(); //DO NOT dispose, because it remove the filestream not the reader and crash the parse
                fileStream = new TIFFBinaryReaderRE(file);
            }

            //read the header
            header = new Header(fileStream, 0);
            //Read the IFD
            ifd = new IFD(fileStream, header.TIFFoffset, true, false);
        }
Exemple #17
0
        public float GetFloat(IFD e1)
        {
            switch (e1.ty)
            {
            case 1:
            case 3:
            case 4:
                return((int)e1.val);

            case 5:
            {
                Int64 old = si.Position;
                si.Position = e1.val;
                float r1 = r.DWord();
                float r2 = r.DWord();
                si.Position = old;
                return((float)r1 / r2);
            }
            }
            throw new InvalidDataException();
        }
Exemple #18
0
        /* Decodes DNG masked areas into blackareas in the image */
        bool DecodeMaskedAreas(IFD raw)
        {
            Tag masked = raw.GetEntry(TagType.MASKEDAREAS);

            if (masked.dataType != TiffDataType.SHORT && masked.dataType != TiffDataType.LONG)
            {
                return(false);
            }

            Int32 nrects = (int)masked.dataCount / 4;

            if (0 == nrects)
            {
                return(false);
            }

            /* Since we may both have short or int, copy it to int array. */
            var rects = masked.GetUIntArray();

            Point2D top = rawImage.fullSize.offset;

            for (int i = 0; i < nrects; i++)
            {
                Point2D topleft     = new Point2D(rects[i * 4 + 1], rects[i * 4]);
                Point2D bottomright = new Point2D(rects[i * 4 + 3], rects[i * 4 + 2]);
                // Is this a horizontal box, only add it if it covers the active width of the image
                if (topleft.width <= top.width && bottomright.width >= (rawImage.fullSize.dim.width + top.width))
                {
                    rawImage.blackAreas.Add(new BlackArea(topleft.height, bottomright.height - topleft.height, false));
                }
                else if (topleft.height <= top.height && bottomright.height >= (rawImage.fullSize.dim.height + top.height))
                {
                    // Is it a vertical box, only add it if it covers the active height of the image
                    rawImage.blackAreas.Add(new BlackArea(topleft.width, bottomright.width - topleft.width, true));
                }
            }
            return(rawImage.blackAreas.Count != 0);
        }
Exemple #19
0
 /// <summary>
 /// Returns the string representation for the given tag id.
 /// </summary>
 public static string GetTagName(IFD ifd, ushort tagid)
 {
     return GetTagName(GetExifTag(ifd, tagid));
 }
Exemple #20
0
        public override void DecodeRaw()
        {
            List <IFD> data = ifd.GetIFDsWithTag(TagType.STRIPOFFSETS);

            if (data.Count == 0)
            {
                Tag model = ifd.GetEntryRecursive(TagType.MODEL);
                if (model != null && model.DataAsString == "DSLR-A100")
                {
                    DecodeA100();
                    return;
                }
                else
                {
                    DecodeCryptedUncompressed();
                    return;
                }
            }
            IFD raw         = data[0];
            int compression = raw.GetEntry(TagType.COMPRESSION).GetInt(0);

            if (1 == compression)
            {
                DecodeUncompressed(raw);
                return;
            }

            if (32767 != compression)
            {
                throw new RawDecoderException("ARW Decoder: Unsupported compression");
            }

            Tag offsets = raw.GetEntry(TagType.STRIPOFFSETS);
            Tag counts  = raw.GetEntry(TagType.STRIPBYTECOUNTS);

            if (offsets.dataCount != 1)
            {
                throw new RawDecoderException("ARW Decoder: Multiple Strips found: " + offsets.dataCount);
            }
            if (counts.dataCount != offsets.dataCount)
            {
                throw new RawDecoderException("ARW Decoder: Byte count number does not match strip size: count:" + counts.dataCount + ", strips:%u " + offsets.dataCount);
            }
            uint width       = raw.GetEntry(TagType.IMAGEWIDTH).GetUInt(0);
            uint height      = raw.GetEntry(TagType.IMAGELENGTH).GetUInt(0);
            int  bitPerPixel = raw.GetEntry(TagType.BITSPERSAMPLE).GetInt(0);

            rawImage.fullSize.ColorDepth = (ushort)bitPerPixel;
            // Sony E-550 marks compressed 8bpp ARW with 12 bit per pixel
            // this makes the compression detect it as a ARW v1.
            // This camera has however another MAKER entry, so we MAY be able
            // to detect it this way in the future.
            data = ifd.GetIFDsWithTag(TagType.MAKE);
            if (data.Count > 1)
            {
                for (Int32 i = 0; i < data.Count; i++)
                {
                    string make = data[i].GetEntry(TagType.MAKE).DataAsString;
                    // Check for maker "SONY" without spaces
                    if (make != "SONY")
                    {
                        bitPerPixel = 8;
                    }
                }
            }

            bool arw1 = counts.GetInt(0) * 8 != width * height * bitPerPixel;

            if (arw1)
            {
                height += 8;
            }

            rawImage.fullSize.dim = new Point2D(width, height);
            rawImage.Init(false);


            UInt16[] curve = new UInt16[0x4001];
            Tag      c     = raw.GetEntry(TagType.SONY_CURVE);

            uint[] sony_curve = { 0, 0, 0, 0, 0, 4095 };

            for (int i = 0; i < 4; i++)
            {
                sony_curve[i + 1] = (uint)(c.GetShort(i) >> 2) & 0xfff;
            }

            for (int i = 0; i < 0x4001; i++)
            {
                curve[i] = (ushort)i;
            }

            for (int i = 0; i < 5; i++)
            {
                for (uint j = sony_curve[i] + 1; j <= sony_curve[i + 1]; j++)
                {
                    curve[j] = (ushort)(curve[j - 1] + (1 << i));
                }
            }
            var table = new TableLookUp(curve, 0x4000, true);

            long c2  = counts.GetUInt(0);
            uint off = offsets.GetUInt(0);

            if (!reader.IsValid(off))
            {
                throw new RawDecoderException("Sony ARW decoder: Data offset after EOF, file probably truncated");
            }

            if (!reader.IsValid(off, c2))
            {
                c2 = reader.BaseStream.Length - off;
            }

            ImageBinaryReader input = new ImageBinaryReader(reader.BaseStream, off);

            if (arw1)
            {
                DecodeARW(input, width, height);
            }
            else
            {
                DecodeARW2(input, width, height, bitPerPixel);
            }

            //table was already applyed
            rawImage.table = null;
        }
Exemple #21
0
 public ExifProperty(ExifTag tag)
 {
     mTag = tag;
     mIFD = ExifTagFactory.GetTagIFD(tag);
 }
Exemple #22
0
    /// <summary>
    ///     Reads the APP1 section containing Exif metadata.
    /// </summary>
    private void ReadExifAPP1()
    {
        // Find the APP1 section containing Exif metadata
        _exifApp1 = Sections.Find(a => a.Marker == JPEGMarker.APP1 &&
                                  a.Header.Length >= 6 &&
                                  Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0");

        // If there is no APP1 section, add a new one after the last APP0 section (if any).
        if (_exifApp1 == null)
        {
            var insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
            if (insertionIndex == -1)
            {
                insertionIndex = 0;
            }

            insertionIndex++;
            _exifApp1 = new JPEGSection(JPEGMarker.APP1);
            Sections.Insert(insertionIndex, _exifApp1);
            if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }

            return;
        }

        var header   = _exifApp1.Header;
        var ifdqueue = new SortedList <int, IFD>();

        _makerNoteOffset = 0;

        // TIFF header
        var tiffoffset = 6;

        if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
        {
            ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
        }
        else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
        {
            ByteOrder = BitConverterEx.ByteOrder.BigEndian;
        }
        else
        {
            throw new NotValidExifFileException();
        }

        // TIFF header may have a different byte order
        BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
        if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
        {
            tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
        }
        else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
        {
            tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
        }
        else
        {
            throw new NotValidExifFileException();
        }

        // Offset to 0th IFD
        var ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);

        ifdqueue.Add(ifd0offset, IFD.Zeroth);

        var conv        = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
        var thumboffset = -1;
        var thumblength = 0;
        var thumbtype   = -1;

        // Read IFDs
        while (ifdqueue.Count != 0)
        {
            var ifdoffset  = tiffoffset + ifdqueue.Keys[0];
            IFD currentifd = ifdqueue.Values[0];
            ifdqueue.RemoveAt(0);

            // Field count
            var fieldcount = conv.ToUInt16(header, ifdoffset);
            for (short i = 0; i < fieldcount; i++)
            {
                // Read field info
                var fieldoffset = ifdoffset + 2 + (12 * i);
                var tag         = conv.ToUInt16(header, fieldoffset);
                var type        = conv.ToUInt16(header, fieldoffset + 2);
                var count       = conv.ToUInt32(header, fieldoffset + 4);
                var value       = new byte[4];
                Array.Copy(header, fieldoffset + 8, value, 0, 4);

                // Fields containing offsets to other IFDs
                if (currentifd == IFD.Zeroth && tag == 0x8769)
                {
                    var exififdpointer = (int)conv.ToUInt32(value, 0);
                    ifdqueue.Add(exififdpointer, IFD.EXIF);
                }
                else if (currentifd == IFD.Zeroth && tag == 0x8825)
                {
                    var gpsifdpointer = (int)conv.ToUInt32(value, 0);
                    ifdqueue.Add(gpsifdpointer, IFD.GPS);
                }
                else if (currentifd == IFD.EXIF && tag == 0xa005)
                {
                    var interopifdpointer = (int)conv.ToUInt32(value, 0);
                    ifdqueue.Add(interopifdpointer, IFD.Interop);
                }

                // Save the offset to maker note data
                if (currentifd == IFD.EXIF && tag == 37500)
                {
                    _makerNoteOffset = conv.ToUInt32(value, 0);
                }

                // Calculate the bytes we need to read
                uint baselength = 0;
                if (type == 1 || type == 2 || type == 7)
                {
                    baselength = 1;
                }
                else if (type == 3)
                {
                    baselength = 2;
                }
                else if (type == 4 || type == 9)
                {
                    baselength = 4;
                }
                else if (type == 5 || type == 10)
                {
                    baselength = 8;
                }

                var totallength = count * baselength;

                // If field value does not fit in 4 bytes
                // the value field is an offset to the actual
                // field value
                var fieldposition = 0;
                if (totallength > 4)
                {
                    fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
                    value         = new byte[totallength];
                    Array.Copy(header, fieldposition, value, 0, totallength);
                }

                // Compressed thumbnail data
                if (currentifd == IFD.First && tag == 0x201)
                {
                    thumbtype   = 0;
                    thumboffset = (int)conv.ToUInt32(value, 0);
                }
                else if (currentifd == IFD.First && tag == 0x202)
                {
                    thumblength = (int)conv.ToUInt32(value, 0);
                }

                // Uncompressed thumbnail data
                if (currentifd == IFD.First && tag == 0x111)
                {
                    thumbtype = 1;

                    // Offset to first strip
                    if (type == 3)
                    {
                        thumboffset = conv.ToUInt16(value, 0);
                    }
                    else
                    {
                        thumboffset = (int)conv.ToUInt32(value, 0);
                    }
                }
                else if (currentifd == IFD.First && tag == 0x117)
                {
                    thumblength = 0;
                    for (var j = 0; j < count; j++)
                    {
                        if (type == 3)
                        {
                            thumblength += conv.ToUInt16(value, 0);
                        }
                        else
                        {
                            thumblength += (int)conv.ToUInt32(value, 0);
                        }
                    }
                }

                // Create the exif property from the interop data
                ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding);
                Properties.Add(prop);
            }

            // 1st IFD pointer
            var firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + (12 * fieldcount));
            if (firstifdpointer != 0)
            {
                ifdqueue.Add(firstifdpointer, IFD.First);
            }

            // Read thumbnail
            if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
            {
                if (thumbtype == 0)
                {
                    using (var ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength))
                    {
                        Thumbnail = FromStream(ts);
                    }
                }
            }
        }
    }
Exemple #23
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </summary>
        private void ReadExifAPP1()
        {
            // Find the APP1 section containing Exif metadata
            exifApp1 = Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                                     a.Header.Length >= 6 &&
                                     (Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"));

            // If there is no APP1 section, add a new one after the last APP0 section (if any).
            if (exifApp1 == null)
            {
                int insertionIndex = Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1)
                {
                    insertionIndex = 0;
                }
                insertionIndex++;
                exifApp1 = new JPEGSection(JPEGMarker.APP1);
                Sections.Insert(insertionIndex, exifApp1);
                if (BitConverterEx.SystemByteOrder == BitConverterEx.ByteOrder.LittleEndian)
                {
                    ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
                }
                else
                {
                    ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                }
                return;
            }

            byte[] header = exifApp1.Header;
            SortedList <int, IFD> ifdqueue = new SortedList <int, IFD>();

            makerNoteOffset = 0;

            // TIFF header
            int tiffoffset = 6;

            if (header[tiffoffset] == 0x49 && header[tiffoffset + 1] == 0x49)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (header[tiffoffset] == 0x4D && header[tiffoffset + 1] == 0x4D)
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

            // TIFF header may have a different byte order
            BitConverterEx.ByteOrder tiffByteOrder = ByteOrder;
            if (BitConverterEx.LittleEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else if (BitConverterEx.BigEndian.ToUInt16(header, tiffoffset + 2) == 42)
            {
                tiffByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            else
            {
                throw new NotValidExifFileException();
            }

            // Offset to 0th IFD
            if (header.Length - (tiffoffset + 4) >= 4)
            {
                int ifd0offset = (int)BitConverterEx.ToUInt32(header, tiffoffset + 4, tiffByteOrder, BitConverterEx.SystemByteOrder);
                ifdqueue.Add(ifd0offset, IFD.Zeroth);
            }

            BitConverterEx conv        = new BitConverterEx(ByteOrder, BitConverterEx.SystemByteOrder);
            int            thumboffset = -1;
            int            thumblength = 0;
            int            thumbtype   = -1;


            // Read IFDs
            while (ifdqueue.Count != 0)
            {
                int ifdoffset  = tiffoffset + ifdqueue.Keys[0];
                IFD currentifd = ifdqueue.Values[0];
                ifdqueue.RemoveAt(0);

                // Field count
                ushort fieldcount = conv.ToUInt16(header, ifdoffset);
                if (ifdoffset > header.Length - 1 || ifdoffset + 2 > header.Length)
                {
                    Errors.Add(new ImageError(Severity.Warning, $"IFD field count overflow for IFD {currentifd}."));
                    continue;
                }
                for (short i = 0; i < fieldcount; i++)
                {
                    // Read field info
                    int    fieldoffset = ifdoffset + 2 + 12 * i;
                    ushort tag         = conv.ToUInt16(header, fieldoffset);
                    ushort type        = conv.ToUInt16(header, fieldoffset + 2);
                    uint   count       = conv.ToUInt32(header, fieldoffset + 4);
                    byte[] value       = new byte[4];
                    Array.Copy(header, fieldoffset + 8, value, 0, 4);

                    // Fields containing offsets to other IFDs
                    if (currentifd == IFD.Zeroth && tag == 0x8769)
                    {
                        int exififdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(exififdpointer, IFD.EXIF);
                    }
                    else if (currentifd == IFD.Zeroth && tag == 0x8825)
                    {
                        int gpsifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(gpsifdpointer, IFD.GPS);
                    }
                    else if (currentifd == IFD.EXIF && tag == 0xa005)
                    {
                        int interopifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(interopifdpointer, IFD.Interop);
                    }

                    // Save the offset to maker note data
                    if (currentifd == IFD.EXIF && tag == 37500)
                    {
                        makerNoteOffset = conv.ToUInt32(value, 0);
                    }

                    // Calculate the bytes we need to read
                    uint baselength = 0;
                    if (type == 1 || type == 2 || type == 6 || type == 7)
                    {
                        baselength = 1;
                    }
                    else if (type == 3 || type == 8)
                    {
                        baselength = 2;
                    }
                    else if (type == 4 || type == 9)
                    {
                        baselength = 4;
                    }
                    else if (type == 5 || type == 10)
                    {
                        baselength = 8;
                    }
                    else // Unknown or invalid type
                    {
                        continue; // Skip and keep going
                    }
                    int totallength = (int)(count * baselength);
                    if (totallength < 0)
                    {
                        Errors.Add(new ImageError(Severity.Warning, $"Field length overflow for tag {tag}."));
                        continue;
                    }

                    // If field value does not fit in 4 bytes
                    // the value field is an offset to the actual
                    // field value
                    int fieldposition = 0;
                    if (totallength > 4)
                    {
                        fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
                        if (fieldposition < 0)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field offset overflow for tag {tag}."));
                            continue;
                        }
                        else if (fieldposition > header.Length - 1)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field offset for tag {tag} exceeds header length."));
                            continue;
                        }
                        else if (fieldposition + totallength > header.Length)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field length for tag {tag} exceeds header length."));
                            continue;
                        }
                        else if (totallength > int.MaxValue)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Field length for tag {tag} exceeds maximum allowed length."));
                            continue;
                        }

                        value = new byte[totallength];
                        Array.Copy(header, fieldposition, value, 0, totallength);
                    }

                    // Compressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x201)
                    {
                        thumbtype   = 0;
                        thumboffset = (int)conv.ToUInt32(value, 0);
                    }
                    else if (currentifd == IFD.First && tag == 0x202)
                    {
                        thumblength = (int)conv.ToUInt32(value, 0);
                    }

                    // Uncompressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x111)
                    {
                        thumbtype = 1;
                        // Offset to first strip
                        if (type == 3)
                        {
                            thumboffset = (int)conv.ToUInt16(value, 0);
                        }
                        else
                        {
                            thumboffset = (int)conv.ToUInt32(value, 0);
                        }
                    }
                    else if (currentifd == IFD.First && tag == 0x117)
                    {
                        thumblength = 0;
                        for (int j = 0; j < count; j++)
                        {
                            if (type == 3)
                            {
                                thumblength += (int)conv.ToUInt16(value, 0);
                            }
                            else
                            {
                                thumblength += (int)conv.ToUInt32(value, 0);
                            }
                        }
                    }

                    // Create the exif property from the interop data
                    ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd, Encoding);
                    Properties.Add(prop);
                }

                // 1st IFD pointer
                if (currentifd == IFD.Zeroth)
                {
                    int firstifdoffset = ifdoffset + 2 + 12 * fieldcount;
                    if (firstifdoffset + 4 <= header.Length)
                    {
                        int firstifdpointer = (int)conv.ToUInt32(header, firstifdoffset);
                        if (firstifdpointer != 0)
                        {
                            if (firstifdpointer + 2 <= header.Length)
                            {
                                ifdqueue.Add(firstifdpointer, IFD.First);
                            }
                            else
                            {
                                Errors.Add(new ImageError(Severity.Warning, $"Invalid first IFD pointer."));
                            }
                        }
                    }
                    else
                    {
                        Errors.Add(new ImageError(Severity.Warning, $"Invalid first IFD offset."));
                    }
                }
                // Read thumbnail
                if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
                {
                    if (thumbtype == 0)
                    {
                        // Ensure that the thumbnail length does not exceed header length
                        if (thumblength > header.Length - tiffoffset - thumboffset)
                        {
                            Errors.Add(new ImageError(Severity.Warning, $"Thumbnail size exceeds header length."));
                            Thumbnail = null;
                        }
                        else
                        {
                            Thumbnail = new byte[thumblength];
                            Array.Copy(header, tiffoffset + thumboffset, Thumbnail, 0, thumblength);
                        }
                    }
                }
            }
        }
Exemple #24
0
 /// <summary>
 /// Returns the ExifTag corresponding to the given tag id.
 /// </summary>
 public static ExifTag GetExifTag(IFD ifd, ushort tagid)
 {
     return (ExifTag)(ifd + tagid);
 }
Exemple #25
0
        /// <summary>
        /// Reads the APP1 section containing Exif metadata.
        /// </summary>
        private void ReadAPP1()
        {
            // Find the APP1 section containing Exif metadata
            app1 = file.Sections.Find(a => (a.Marker == JPEGMarker.APP1) &&
                                      (Encoding.ASCII.GetString(a.Header, 0, 6) == "Exif\0\0"));

            // If there is no APP1 section, add a new one after the last APP0 section (if any).
            if (app1 == null)
            {
                int insertionIndex = file.Sections.FindLastIndex(a => a.Marker == JPEGMarker.APP0);
                if (insertionIndex == -1)
                {
                    insertionIndex = 0;
                }
                insertionIndex++;
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
                app1      = new JPEGSection(JPEGMarker.APP1);
                file.Sections.Insert(insertionIndex, app1);
                return;
            }
//#if DEBUG
//            mMap = new MemoryBinStream();
//            mMap.Seek(0, SeekOrigin.Begin);
//            mMap.Write(new Bin("Exif Marker", 3, 6));
//#endif

            byte[] header = app1.Header;
            SortedList <int, IFD> ifdqueue = new SortedList <int, IFD>();

            makerNoteOffset = 0;

            // TIFF header
            int tiffoffset = 6;

            if (header[tiffoffset] == 0x49)
            {
                ByteOrder = BitConverterEx.ByteOrder.LittleEndian;
            }
            else
            {
                ByteOrder = BitConverterEx.ByteOrder.BigEndian;
            }
            BitConverterEx conv = new BitConverterEx(ByteOrder, BitConverterEx.ByteOrder.System);

            // Offset to 0th IFD
            int ifd0offset = (int)conv.ToUInt32(header, tiffoffset + 4);

            ifdqueue.Add(ifd0offset, IFD.Zeroth);

            int thumboffset = -1;
            int thumblength = 0;
            int thumbtype   = -1;

#if DEBUG
            mMap.Write(new Bin("Byte Order: " + ByteOrder.ToString(), 4, 2));
            mMap.Write(new Bin("TIFF ID: 0x00, 0x2A", 4, 2));
            mMap.Write(new Bin("Offset to 0th IFD: " + ifd0offset.ToString(), 4, 4));
#endif
            // Read IFDs
            while (ifdqueue.Count != 0)
            {
                int ifdoffset  = tiffoffset + ifdqueue.Keys[0];
                IFD currentifd = ifdqueue.Values[0];
                ifdqueue.RemoveAt(0);

                // Field count
                ushort fieldcount = conv.ToUInt16(header, ifdoffset);
#if DEBUG
                mMap.Seek(ifdoffset, SeekOrigin.Begin);
                mMap.Write(new Bin(currentifd.ToString() + " IFD Field Count: " + fieldcount.ToString(), 5, 2));
#endif
                for (short i = 0; i < fieldcount; i++)
                {
                    // Read field info
                    int    fieldoffset = ifdoffset + 2 + 12 * i;
                    ushort tag         = conv.ToUInt16(header, fieldoffset);
                    ushort type        = conv.ToUInt16(header, fieldoffset + 2);
                    uint   count       = conv.ToUInt32(header, fieldoffset + 4);
                    byte[] value       = new byte[4];
                    Array.Copy(header, fieldoffset + 8, value, 0, 4);

                    // Fields containing offsets to other IFDs
                    if (currentifd == IFD.Zeroth && tag == 0x8769)
                    {
                        int exififdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(exififdpointer, IFD.EXIF);
                    }
                    else if (currentifd == IFD.Zeroth && tag == 0x8825)
                    {
                        int gpsifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(gpsifdpointer, IFD.GPS);
                    }
                    else if (currentifd == IFD.EXIF && tag == 0xa005)
                    {
                        int interopifdpointer = (int)conv.ToUInt32(value, 0);
                        ifdqueue.Add(interopifdpointer, IFD.Interop);
                    }

                    // Save the offset to maker note data
                    if (currentifd == IFD.EXIF && tag == 37500)
                    {
                        makerNoteOffset = conv.ToUInt32(value, 0);
                    }

                    // Calculate the bytes we need to read
                    uint baselength = 0;
                    if (type == 1 || type == 2 || type == 7)
                    {
                        baselength = 1;
                    }
                    else if (type == 3)
                    {
                        baselength = 2;
                    }
                    else if (type == 4 || type == 9)
                    {
                        baselength = 4;
                    }
                    else if (type == 5 || type == 10)
                    {
                        baselength = 8;
                    }
                    uint totallength = count * baselength;

                    // If field value does not fit in 4 bytes
                    // the value field is an offset to the actual
                    // field value
                    int fieldposition = 0;
                    if (totallength > 4)
                    {
                        fieldposition = tiffoffset + (int)conv.ToUInt32(value, 0);
                        value         = new byte[totallength];
                        Array.Copy(header, fieldposition, value, 0, totallength);
                    }

                    // Compressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x201)
                    {
                        thumbtype   = 0;
                        thumboffset = (int)conv.ToUInt32(value, 0);
                    }
                    else if (currentifd == IFD.First && tag == 0x202)
                    {
                        thumblength = (int)conv.ToUInt32(value, 0);
                    }

                    // Uncompressed thumbnail data
                    if (currentifd == IFD.First && tag == 0x111)
                    {
                        thumbtype = 1;
                        // Offset to first strip
                        if (type == 3)
                        {
                            thumboffset = (int)conv.ToUInt16(value, 0);
                        }
                        else
                        {
                            thumboffset = (int)conv.ToUInt32(value, 0);
                        }
                    }
                    else if (currentifd == IFD.First && tag == 0x117)
                    {
                        thumblength = 0;
                        for (int j = 0; j < count; j++)
                        {
                            if (type == 3)
                            {
                                thumblength += (int)conv.ToUInt16(value, 0);
                            }
                            else
                            {
                                thumblength += (int)conv.ToUInt32(value, 0);
                            }
                        }
                    }

                    // Create the exif property from the interop data
                    ExifProperty prop = ExifPropertyFactory.Get(tag, type, count, value, ByteOrder, currentifd);
                    Properties.Add(prop.Tag, prop);
#if DEBUG
                    mMap.Seek(fieldoffset, SeekOrigin.Begin);
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " ID: " + tag.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Type: " + type.ToString(), 6, 2, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Count: " + count.ToString(), 6, 4, prop));
                    mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Value: " + string.Format("[0x{0:x2}, 0x{1:x2}, 0x{2:x2}, 0x{3:x2}]", value[0], value[1], value[2], value[3]), 6, 4, prop));
                    if (totallength > 4)
                    {
                        mMap.Seek(fieldposition, SeekOrigin.Begin);
                        mMap.Write(new Bin(ExifTagFactory.GetTagName(currentifd, tag) + " Data", 7, totallength, prop));
                    }
#endif
                }

                // 1st IFD pointer
                int firstifdpointer = (int)conv.ToUInt32(header, ifdoffset + 2 + 12 * fieldcount);
                if (firstifdpointer != 0)
                {
                    ifdqueue.Add(firstifdpointer, IFD.First);
                }
#if DEBUG
                mMap.Seek(ifdoffset + 2 + 12 * fieldcount, SeekOrigin.Begin);
                mMap.Write(new Bin("1st IFD Pointer: " + firstifdpointer.ToString(), 5, 4));
#endif
                // Read thumbnail
                if (thumboffset != -1 && thumblength != 0 && Thumbnail == null)
                {
                    if (thumbtype == 0)
                    {
                        using (MemoryStream ts = new MemoryStream(header, tiffoffset + thumboffset, thumblength))
                        {
                            Thumbnail = new JPEGFile(ts);
                        }
                    }
#if DEBUG
                    mMap.Seek(tiffoffset + thumboffset, SeekOrigin.Begin);
                    mMap.Write(new Bin("Thumbnail", 8, thumblength));
#endif
                }
            }
        }
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.ByteOrder.System);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                    return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value));
                else if (tag == 0xa000) // FlashpixVersion
                    return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value));
                else if (tag == 0xa001) // ColorSpace
                    return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
                else if (tag == 0x9286) // UserComment
                {
                    byte[] encbytes = new byte[8];
                    byte[] strbytes = new byte[value.Length - 8];
                    Array.Copy(value, encbytes, 8);
                    Array.Copy(value, 8, strbytes, 0, value.Length - 8);
                    Encoding enc = Encoding.UTF8;
                    string encstr = enc.GetString(encbytes, 0, encbytes.Length);
                    if (encstr == "ASCII\0\0\0")
                        enc = Encoding.UTF8;
                    else if (encstr == "JIS\0\0\0\0\0")
                        enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                    else if (encstr == "Unicode\0")
                        enc = Encoding.Unicode;
                    else
                        enc = null;

                    int len = Array.IndexOf(strbytes, (byte)0);
                    if (len == -1) len = strbytes.Length;
                    return new ExifEncodedString(ExifTag.UserComment, (enc == null ? Encoding.UTF8.GetString(strbytes, 0, len) : enc.GetString(strbytes, 0, len)), enc);
                }
                else if (tag == 0x9003) // DateTimeOriginal
                    return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9004) // DateTimeDigitized
                    return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x8822) // ExposureProgram
                    return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
                else if (tag == 0x9207) // MeteringMode
                    return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
                else if (tag == 0x9208) // LightSource
                    return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
                else if (tag == 0x9209) // Flash
                    return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                        return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else if (count == 4)
                        return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else // count == 2
                        return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa214) // SubjectLocation
                    return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                else if (tag == 0xa217) // SensingMethod
                    return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa300) // FileSource
                    return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa301) // SceneType
                    return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa401) // CustomRendered
                    return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa402) // ExposureMode
                    return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa403) // WhiteBalance
                    return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa406) // SceneCaptureType
                    return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa407) // GainControl
                    return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa408) // Contrast
                    return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa409) // Saturation
                    return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40a) // Sharpness
                    return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40c) // SubjectDistanceRange
                    return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistance, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                    return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
                else if (tag == 1) // GPSLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 2) // GPSLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 3) // GPSLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 4) // GPSLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 5) // GPSAltitudeRef
                    return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
                else if (tag == 7) // GPSTimeStamp
                    return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 9) // GPSStatus
                    return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
                else if (tag == 10) // GPSMeasureMode
                    return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
                else if (tag == 12) // GPSSpeedRef
                    return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
                else if (tag == 14) // GPSTrackRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
                else if (tag == 16) // GPSImgDirectionRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
                else if (tag == 19) // GPSDestLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 20) // GPSDestLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 21) // GPSDestLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 22) // GPSDestLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 23) // GPSDestBearingRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
                else if (tag == 25) // GPSDestDistanceRef
                    return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
                else if (tag == 29) // GPSDate
                    return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
                else if (tag == 30) // GPSDifferential
                    return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                    return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value));
                else if (tag == 2) // InteroperabilityVersion
                    return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value));
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
            }

            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                    return new ExifByte(etag, value[0]);
                else
                    return new ExifByteArray(etag, value);
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return new ExifAscii(etag, ExifBitConverter.ToAscii(value));
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUShort(etag, conv.ToUInt16(value, 0));
                else
                    return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUInt(etag, conv.ToUInt32(value, 0));
                else
                    return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                    return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
                else
                    return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
                return new ExifUndefined(etag, value);
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                    return new ExifSInt(etag, conv.ToInt32(value, 0));
                else
                    return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                    return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
                else
                    return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
            }
            else
                throw new ArgumentException("Unknown property type.");
        }
Exemple #27
0
        private void WriteIFD(MemoryStream stream, Dictionary<ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
        {
            BitConverterEx conv = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a queue of fields to write
            Queue<ExifProperty> fieldqueue = new Queue<ExifProperty>();
            foreach (ExifProperty prop in ifd.Values)
                if (prop.Tag != ExifTag.MakerNote)
                    fieldqueue.Enqueue(prop);
            // Push the maker note data to the end
            if (ifd.ContainsKey(ExifTag.MakerNote))
                fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);

            // Offset to start of field data from start of TIFF header
            uint dataoffset = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
            uint currentdataoffset = dataoffset;
            long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);

            bool makernotewritten = false;
            // Field count
            stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
            // Fields
            while (fieldqueue.Count != 0)
            {
                ExifProperty field = fieldqueue.Dequeue();
                ExifInterOperability interop = field.Interoperability;

                uint fillerbytecount = 0;

                // Try to preserve the makernote data offset
                if (!makernotewritten &&
                    !makerNoteProcessed &&
                    makerNoteOffset != 0 &&
                    ifdtype == IFD.EXIF &&
                    field.Tag != ExifTag.MakerNote &&
                    interop.Data.Length > 4 &&
                    currentdataoffset + interop.Data.Length > makerNoteOffset &&
                    ifd.ContainsKey(ExifTag.MakerNote))
                {
                    // Delay writing this field until we write makernote data
                    fieldqueue.Enqueue(field);
                    continue;
                }
                else if (field.Tag == ExifTag.MakerNote)
                {
                    makernotewritten = true;
                    // We may need to write filler bytes to preserve maker note offset
                    if (preserveMakerNote && !makerNoteProcessed)
                        fillerbytecount = makerNoteOffset - currentdataoffset;
                    else
                        fillerbytecount = 0;
                }

                // Tag
                stream.Write(conv.GetBytes(interop.TagID), 0, 2);
                // Type
                stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
                // Count
                stream.Write(conv.GetBytes(interop.Count), 0, 4);
                // Field data
                byte[] data = interop.Data;
                if (ByteOrder != BitConverterEx.SystemByteOrder)
                {
                    if (interop.TypeID == 1 || interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9)
                        Array.Reverse(data);
                    else if (interop.TypeID == 5 || interop.TypeID == 10)
                    {
                        Array.Reverse(data, 0, 4);
                        Array.Reverse(data, 4, 4);
                    }
                }

                // Fields containing offsets to other IFDs
                // Just store their offets, we will write the values later on when we know the lengths of IFDs
                if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
                    exifIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
                    gpsIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
                    interopIFDFieldOffset = stream.Position;
                else if (ifdtype == IFD.First && interop.TagID == 0x201)
                    thumbOffsetLocation = stream.Position;
                else if (ifdtype == IFD.First && interop.TagID == 0x202)
                    thumbSizeLocation = stream.Position;

                // Write 4 byte field value or field data
                if (data.Length <= 4)
                {
                    stream.Write(data, 0, data.Length);
                    for (int i = data.Length; i < 4; i++)
                        stream.WriteByte(0);
                }
                else
                {
                    // Pointer to data area relative to TIFF header
                    stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
                    // Actual data
                    long currentoffset = stream.Position;
                    stream.Seek(absolutedataoffset, SeekOrigin.Begin);
                    // Write filler bytes
                    for (int i = 0; i < fillerbytecount; i++)
                        stream.WriteByte(0xFF);
                    stream.Write(data, 0, data.Length);
                    stream.Seek(currentoffset, SeekOrigin.Begin);
                    // Increment pointers
                    currentdataoffset += fillerbytecount + (uint)data.Length;
                    absolutedataoffset += fillerbytecount + data.Length;
                }
            }
            // Offset to 1st IFD
            // We will write zeros for now. This will be filled after we write all IFDs
            if (ifdtype == IFD.Zeroth)
                firstIFDFieldOffset = stream.Position;
            stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);

            // Seek to end of IFD
            stream.Seek(absolutedataoffset, SeekOrigin.Begin);

            // Write thumbnail data
            if (ifdtype == IFD.First)
            {
                if (Thumbnail != null)
                {
                    MemoryStream ts = new MemoryStream();
                    Thumbnail.Save(ts);
                    ts.Close();
                    byte[] thumb = ts.ToArray();
                    thumbOffsetValue = (uint)(stream.Position - tiffoffset);
                    thumbSizeValue = (uint)thumb.Length;
                    stream.Write(thumb, 0, thumb.Length);
                    ts.Dispose();
                }
                else
                {
                    thumbOffsetValue = 0;
                    thumbSizeValue = 0;
                }
            }
        }
Exemple #28
0
        void GetWB()
        {
            // Set the whitebalance for all the modern ARW formats (everything after A100)
            Tag priv = ifd.GetEntryRecursive(TagType.DNGPRIVATEDATA);

            if (priv != null)
            {
                byte[] data = priv.GetByteArray();
                uint   off  = ((((uint)(data)[3]) << 24) | (((uint)(data)[2]) << 16) | (((uint)(data)[1]) << 8) | (data)[0]);
                IFD    sony_private;
                sony_private = new IFD(reader, off, ifd.endian, ifd.Depth);

                Tag sony_offset = sony_private.GetEntryRecursive(TagType.SONY_OFFSET);
                Tag sony_length = sony_private.GetEntryRecursive(TagType.SONY_LENGTH);
                Tag sony_key    = sony_private.GetEntryRecursive(TagType.SONY_KEY);
                if (sony_offset == null || sony_length == null || sony_key == null || sony_key.dataCount != 4)
                {
                    throw new RawDecoderException("Couldn't find the correct metadata for white balance decoding");
                }

                off = sony_offset.GetUInt(0);
                uint len = sony_length.GetUInt(0);
                data = sony_key.GetByteArray();
                uint key = (uint)((data[3] << 24) | (data[2] << 16) | (data[1] << 8) | (data)[0]);
                reader.BaseStream.Position = off;
                byte[] ifp_data = reader.ReadBytes((int)len);
                SonyDecrypt(ifp_data, len / 4, key);
                using (var reader = new ImageBinaryReader(ifp_data))
                {
                    sony_private = new IFD(reader, 0, ifd.endian, 0, -(int)off);
                }
                Tag wb = sony_private.GetEntry(TagType.SONYGRBGLEVELS);
                if (wb != null)
                {
                    if (wb.dataCount != 4)
                    {
                        throw new RawDecoderException("White balance has " + wb.dataCount + " entries instead of 4");
                    }
                    rawImage.metadata.WbCoeffs = new WhiteBalance(wb.GetInt(1), wb.GetInt(0), wb.GetInt(2), rawImage.fullSize.ColorDepth);
                }
                else if ((wb = sony_private.GetEntry(TagType.SONYRGGBLEVELS)) != null)
                {
                    if (wb.dataCount != 4)
                    {
                        throw new RawDecoderException("White balance has " + wb.dataCount + " entries instead of 4");
                    }
                    rawImage.metadata.WbCoeffs = new WhiteBalance(wb.GetInt(0), wb.GetInt(1), wb.GetInt(3), rawImage.fullSize.ColorDepth);
                }

                //TODO read the color matrix 0x7800

                Tag black = sony_private.GetEntry((TagType)0x7300) ?? sony_private.GetEntry((TagType)0x7310);
                if (black != null)
                {
                    rawImage.black = black.GetLong(0);
                }

                Tag white = sony_private.GetEntry((TagType)0x787f);
                if (white != null)
                {
                    rawImage.whitePoint = white.GetLong(0);
                }
            }
        }
Exemple #29
0
 public IFDAttribute(IFD ifd)
 {
     IFD = ifd;
 }
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9c9b || tag == 0x9c9c ||  // Windows tags
                    tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
                    return new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0'));
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                    return new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
                else if (tag == 0xa000) // FlashpixVersion
                    return new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
                else if (tag == 0xa001) // ColorSpace
                    return new ExifEnumProperty<ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0));
                else if (tag == 0x9286) // UserComment
                {
                    // Default to ASCII
                    Encoding enc = Encoding.ASCII;
                    bool hasenc;
                    if (value.Length < 8)
                        hasenc = false;
                    else
                    {
                        hasenc = true;
                        string encstr = enc.GetString(value, 0, 8);
                        if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.ASCII;
                        else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                        else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
                            enc = Encoding.Unicode;
                        else
                            hasenc = false;
                    }

                    string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');

                    return new ExifEncodedString(ExifTag.UserComment, val, enc);
                }
                else if (tag == 0x9003) // DateTimeOriginal
                    return new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x9004) // DateTimeDigitized
                    return new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value));
                else if (tag == 0x8822) // ExposureProgram
                    return new ExifEnumProperty<ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0));
                else if (tag == 0x9207) // MeteringMode
                    return new ExifEnumProperty<MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0));
                else if (tag == 0x9208) // LightSource
                    return new ExifEnumProperty<LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0));
                else if (tag == 0x9209) // Flash
                    return new ExifEnumProperty<Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true);
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                        return new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else if (count == 4)
                        return new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                    else // count == 2
                        return new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa214) // SubjectLocation
                    return new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
                else if (tag == 0xa217) // SensingMethod
                    return new ExifEnumProperty<SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa300) // FileSource
                    return new ExifEnumProperty<FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa301) // SceneType
                    return new ExifEnumProperty<SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa401) // CustomRendered
                    return new ExifEnumProperty<CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa402) // ExposureMode
                    return new ExifEnumProperty<ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa403) // WhiteBalance
                    return new ExifEnumProperty<WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa406) // SceneCaptureType
                    return new ExifEnumProperty<SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa407) // GainControl
                    return new ExifEnumProperty<GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa408) // Contrast
                    return new ExifEnumProperty<Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa409) // Saturation
                    return new ExifEnumProperty<Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40a) // Sharpness
                    return new ExifEnumProperty<Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true);
                else if (tag == 0xa40c) // SubjectDistanceRange
                    return new ExifEnumProperty<SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true);
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                    return new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value));
                else if (tag == 1) // GPSLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 2) // GPSLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 3) // GPSLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 4) // GPSLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 5) // GPSAltitudeRef
                    return new ExifEnumProperty<GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]);
                else if (tag == 7) // GPSTimeStamp
                    return new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 9) // GPSStatus
                    return new ExifEnumProperty<GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]);
                else if (tag == 10) // GPSMeasureMode
                    return new ExifEnumProperty<GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]);
                else if (tag == 12) // GPSSpeedRef
                    return new ExifEnumProperty<GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]);
                else if (tag == 14) // GPSTrackRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]);
                else if (tag == 16) // GPSImgDirectionRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]);
                else if (tag == 19) // GPSDestLatitudeRef
                    return new ExifEnumProperty<GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]);
                else if (tag == 20) // GPSDestLatitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 21) // GPSDestLongitudeRef
                    return new ExifEnumProperty<GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]);
                else if (tag == 22) // GPSDestLongitude
                    return new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
                else if (tag == 23) // GPSDestBearingRef
                    return new ExifEnumProperty<GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]);
                else if (tag == 25) // GPSDestDistanceRef
                    return new ExifEnumProperty<GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]);
                else if (tag == 29) // GPSDate
                    return new ExifDateTime(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false));
                else if (tag == 30) // GPSDifferential
                    return new ExifEnumProperty<GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0));
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                    return new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII);
                else if (tag == 2) // InteroperabilityVersion
                    return new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII));
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                    return new ExifEnumProperty<Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0));
                else if (tag == 0x106) // PhotometricInterpretation
                    return new ExifEnumProperty<PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0));
                else if (tag == 0x112) // Orientation
                    return new ExifEnumProperty<Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0));
                else if (tag == 0x11c) // PlanarConfiguration
                    return new ExifEnumProperty<PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0));
                else if (tag == 0x213) // YCbCrPositioning
                    return new ExifEnumProperty<YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0));
                else if (tag == 0x128) // ResolutionUnit
                    return new ExifEnumProperty<ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0));
                else if (tag == 0x132) // DateTime
                    return new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value));
            }

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                    return new ExifByte(etag, value[0]);
                else
                    return new ExifByteArray(etag, value);
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding);
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUShort(etag, conv.ToUInt16(value, 0));
                else
                    return new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder));
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                    return new ExifUInt(etag, conv.ToUInt32(value, 0));
                else
                    return new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder));
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                    return new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder));
                else
                    return new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder));
            }
            else if (type == 6) // 6 = SBYTE An 8-bit signed (twos-complement) integer.
            {
                if (count == 1)
                    return new ExifSByte(etag, Convert.ToSByte(value[0]));
                else
                    return new ExifSByteArray(etag, Array.ConvertAll(value, b => Convert.ToSByte(b)));
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
            {
                return new ExifUndefined(etag, value);
            }
            else if (type == 8) // 8 = SSHORT A 16-bit (2-byte) signed short
            {
                if (count == 1)
                    return new ExifSShort(etag, conv.ToInt16(value, 0));
                else
                    return new ExifSShortArray(etag, ExifBitConverter.ToSShortArray(value, (int)count, byteOrder));
            }
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                    return new ExifSInt(etag, conv.ToInt32(value, 0));
                else
                    return new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder));
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                    return new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder));
                else
                    return new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder));
            }
            else if (type == 11) // 11 = FLOAT A single precision (4-byte) IEEE format
            {
                if (count == 1)
                    return new ExifFloat(etag, conv.ToSingle(value, 0));
                else
                    return new ExifFloatArray(etag, ExifBitConverter.ToSingleArray(value, (int)count, byteOrder));
            }
            else if (type == 12) // 12 = DOUBLE A double precision (8-byte) IEEE format
            {
                if (count == 1)
                    return new ExifDouble(etag, conv.ToDouble(value, 0));
                else
                    return new ExifDoubleArray(etag, ExifBitConverter.ToDoubleArray(value, (int)count, byteOrder));
            }
            else
                throw new ArgumentException("Unknown property type.");
        }
        /// <summary>
        /// Creates an ExifProperty from the given interoperability parameters.
        /// </summary>
        /// <param name="tag">The tag id of the exif property.</param>
        /// <param name="type">The type id of the exif property.</param>
        /// <param name="count">Byte or component count.</param>
        /// <param name="value">Field data as an array of bytes.</param>
        /// <param name="byteOrder">Byte order of value.</param>
        /// <param name="ifd">IFD section containing this propery.</param>
        /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param>
        /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
        public static ExifProperty Get(ushort tag, ushort type, uint count, byte[] value, BitConverterEx.ByteOrder byteOrder, IFD ifd, Encoding encoding)
        {
            BitConverterEx conv = new BitConverterEx(byteOrder, BitConverterEx.SystemByteOrder);
            // Find the exif tag corresponding to given tag id
            ExifTag etag = ExifTagFactory.GetExifTag(ifd, tag);

            if (ifd == IFD.Zeroth)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.Compression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.PhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.Orientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.PlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.YCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.DateTime, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x9c9b || tag == 0x9c9c ||  // Windows tags
                         tag == 0x9c9d || tag == 0x9c9e || tag == 0x9c9f)
                {
                    return(new WindowsByteString(etag, Encoding.Unicode.GetString(value).TrimEnd('\0')));
                }
            }
            else if (ifd == IFD.EXIF)
            {
                if (tag == 0x9000) // ExifVersion
                {
                    return(new ExifVersion(ExifTag.ExifVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
                else if (tag == 0xa000) // FlashpixVersion
                {
                    return(new ExifVersion(ExifTag.FlashpixVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
                else if (tag == 0xa001) // ColorSpace
                {
                    return(new ExifEnumProperty <ColorSpace>(ExifTag.ColorSpace, (ColorSpace)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9286) // UserComment
                {
                    // Default to ASCII
                    Encoding enc = Encoding.ASCII;
                    bool     hasenc;
                    if (value.Length < 8)
                    {
                        hasenc = false;
                    }
                    else
                    {
                        hasenc = true;
                        string encstr = enc.GetString(value, 0, 8);
                        if (string.Compare(encstr, "ASCII\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.ASCII;
                        }
                        else if (string.Compare(encstr, "JIS\0\0\0\0\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.GetEncoding("Japanese (JIS 0208-1990 and 0212-1990)");
                        }
                        else if (string.Compare(encstr, "Unicode\0", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            enc = Encoding.Unicode;
                        }
                        else
                        {
                            hasenc = false;
                        }
                    }

                    string val = (hasenc ? enc.GetString(value, 8, value.Length - 8) : enc.GetString(value)).Trim('\0');

                    return(new ExifEncodedString(ExifTag.UserComment, val, enc));
                }
                else if (tag == 0x9003) // DateTimeOriginal
                {
                    return(new ExifDateTime(ExifTag.DateTimeOriginal, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x9004) // DateTimeDigitized
                {
                    return(new ExifDateTime(ExifTag.DateTimeDigitized, ExifBitConverter.ToDateTime(value)));
                }
                else if (tag == 0x8822) // ExposureProgram
                {
                    return(new ExifEnumProperty <ExposureProgram>(ExifTag.ExposureProgram, (ExposureProgram)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9207) // MeteringMode
                {
                    return(new ExifEnumProperty <MeteringMode>(ExifTag.MeteringMode, (MeteringMode)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9208) // LightSource
                {
                    return(new ExifEnumProperty <LightSource>(ExifTag.LightSource, (LightSource)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x9209) // Flash
                {
                    return(new ExifEnumProperty <Flash>(ExifTag.Flash, (Flash)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0x9214) // SubjectArea
                {
                    if (count == 3)
                    {
                        return(new ExifCircularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else if (count == 4)
                    {
                        return(new ExifRectangularSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                    else // count == 2
                    {
                        return(new ExifPointSubjectArea(ExifTag.SubjectArea, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                    }
                }
                else if (tag == 0xa210) // FocalPlaneResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.FocalPlaneResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa214) // SubjectLocation
                {
                    return(new ExifPointSubjectArea(ExifTag.SubjectLocation, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
                else if (tag == 0xa217) // SensingMethod
                {
                    return(new ExifEnumProperty <SensingMethod>(ExifTag.SensingMethod, (SensingMethod)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa300) // FileSource
                {
                    return(new ExifEnumProperty <FileSource>(ExifTag.FileSource, (FileSource)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa301) // SceneType
                {
                    return(new ExifEnumProperty <SceneType>(ExifTag.SceneType, (SceneType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa401) // CustomRendered
                {
                    return(new ExifEnumProperty <CustomRendered>(ExifTag.CustomRendered, (CustomRendered)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa402) // ExposureMode
                {
                    return(new ExifEnumProperty <ExposureMode>(ExifTag.ExposureMode, (ExposureMode)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa403) // WhiteBalance
                {
                    return(new ExifEnumProperty <WhiteBalance>(ExifTag.WhiteBalance, (WhiteBalance)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa406) // SceneCaptureType
                {
                    return(new ExifEnumProperty <SceneCaptureType>(ExifTag.SceneCaptureType, (SceneCaptureType)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa407) // GainControl
                {
                    return(new ExifEnumProperty <GainControl>(ExifTag.GainControl, (GainControl)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa408) // Contrast
                {
                    return(new ExifEnumProperty <Contrast>(ExifTag.Contrast, (Contrast)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa409) // Saturation
                {
                    return(new ExifEnumProperty <Saturation>(ExifTag.Saturation, (Saturation)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40a) // Sharpness
                {
                    return(new ExifEnumProperty <Sharpness>(ExifTag.Sharpness, (Sharpness)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa40c) // SubjectDistanceRange
                {
                    return(new ExifEnumProperty <SubjectDistanceRange>(ExifTag.SubjectDistanceRange, (SubjectDistanceRange)conv.ToUInt16(value, 0), true));
                }
                else if (tag == 0xa432) // LensSpecification
                {
                    return(new LensSpecification(ExifTag.LensSpecification, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (ifd == IFD.GPS)
            {
                if (tag == 0) // GPSVersionID
                {
                    return(new ExifVersion(ExifTag.GPSVersionID, ExifBitConverter.ToString(value)));
                }
                else if (tag == 1) // GPSLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 2) // GPSLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 3) // GPSLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 4) // GPSLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 5) // GPSAltitudeRef
                {
                    return(new ExifEnumProperty <GPSAltitudeRef>(ExifTag.GPSAltitudeRef, (GPSAltitudeRef)value[0]));
                }
                else if (tag == 7) // GPSTimeStamp
                {
                    return(new GPSTimeStamp(ExifTag.GPSTimeStamp, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 9) // GPSStatus
                {
                    return(new ExifEnumProperty <GPSStatus>(ExifTag.GPSStatus, (GPSStatus)value[0]));
                }
                else if (tag == 10) // GPSMeasureMode
                {
                    return(new ExifEnumProperty <GPSMeasureMode>(ExifTag.GPSMeasureMode, (GPSMeasureMode)value[0]));
                }
                else if (tag == 12) // GPSSpeedRef
                {
                    return(new ExifEnumProperty <GPSSpeedRef>(ExifTag.GPSSpeedRef, (GPSSpeedRef)value[0]));
                }
                else if (tag == 14) // GPSTrackRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSTrackRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 16) // GPSImgDirectionRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSImgDirectionRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 19) // GPSDestLatitudeRef
                {
                    return(new ExifEnumProperty <GPSLatitudeRef>(ExifTag.GPSDestLatitudeRef, (GPSLatitudeRef)value[0]));
                }
                else if (tag == 20) // GPSDestLatitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLatitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 21) // GPSDestLongitudeRef
                {
                    return(new ExifEnumProperty <GPSLongitudeRef>(ExifTag.GPSDestLongitudeRef, (GPSLongitudeRef)value[0]));
                }
                else if (tag == 22) // GPSDestLongitude
                {
                    return(new GPSLatitudeLongitude(ExifTag.GPSDestLongitude, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
                else if (tag == 23) // GPSDestBearingRef
                {
                    return(new ExifEnumProperty <GPSDirectionRef>(ExifTag.GPSDestBearingRef, (GPSDirectionRef)value[0]));
                }
                else if (tag == 25) // GPSDestDistanceRef
                {
                    return(new ExifEnumProperty <GPSDistanceRef>(ExifTag.GPSDestDistanceRef, (GPSDistanceRef)value[0]));
                }
                else if (tag == 29) // GPSDateStamp
                {
                    return(new ExifDate(ExifTag.GPSDateStamp, ExifBitConverter.ToDateTime(value, false)));
                }
                else if (tag == 30) // GPSDifferential
                {
                    return(new ExifEnumProperty <GPSDifferential>(ExifTag.GPSDifferential, (GPSDifferential)conv.ToUInt16(value, 0)));
                }
            }
            else if (ifd == IFD.Interop)
            {
                if (tag == 1) // InteroperabilityIndex
                {
                    return(new ExifAscii(ExifTag.InteroperabilityIndex, ExifBitConverter.ToAscii(value, Encoding.ASCII), Encoding.ASCII));
                }
                else if (tag == 2) // InteroperabilityVersion
                {
                    return(new ExifVersion(ExifTag.InteroperabilityVersion, ExifBitConverter.ToAscii(value, Encoding.ASCII)));
                }
            }
            else if (ifd == IFD.First)
            {
                if (tag == 0x103) // Compression
                {
                    return(new ExifEnumProperty <Compression>(ExifTag.ThumbnailCompression, (Compression)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x106) // PhotometricInterpretation
                {
                    return(new ExifEnumProperty <PhotometricInterpretation>(ExifTag.ThumbnailPhotometricInterpretation, (PhotometricInterpretation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x112) // Orientation
                {
                    return(new ExifEnumProperty <Orientation>(ExifTag.ThumbnailOrientation, (Orientation)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x11c) // PlanarConfiguration
                {
                    return(new ExifEnumProperty <PlanarConfiguration>(ExifTag.ThumbnailPlanarConfiguration, (PlanarConfiguration)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x213) // YCbCrPositioning
                {
                    return(new ExifEnumProperty <YCbCrPositioning>(ExifTag.ThumbnailYCbCrPositioning, (YCbCrPositioning)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x128) // ResolutionUnit
                {
                    return(new ExifEnumProperty <ResolutionUnit>(ExifTag.ThumbnailResolutionUnit, (ResolutionUnit)conv.ToUInt16(value, 0)));
                }
                else if (tag == 0x132) // DateTime
                {
                    return(new ExifDateTime(ExifTag.ThumbnailDateTime, ExifBitConverter.ToDateTime(value)));
                }
            }

            if (type == 1) // 1 = BYTE An 8-bit unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifByte(etag, value[0]));
                }
                else
                {
                    return(new ExifByteArray(etag, value));
                }
            }
            else if (type == 2) // 2 = ASCII An 8-bit byte containing one 7-bit ASCII code.
            {
                return(new ExifAscii(etag, ExifBitConverter.ToAscii(value, encoding), encoding));
            }
            else if (type == 3) // 3 = SHORT A 16-bit (2-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUShort(etag, conv.ToUInt16(value, 0)));
                }
                else
                {
                    return(new ExifUShortArray(etag, ExifBitConverter.ToUShortArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 4) // 4 = LONG A 32-bit (4-byte) unsigned integer.
            {
                if (count == 1)
                {
                    return(new ExifUInt(etag, conv.ToUInt32(value, 0)));
                }
                else
                {
                    return(new ExifUIntArray(etag, ExifBitConverter.ToUIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 5) // 5 = RATIONAL Two LONGs. The first LONG is the numerator and the second LONG expresses the denominator.
            {
                if (count == 1)
                {
                    return(new ExifURational(etag, ExifBitConverter.ToURational(value, byteOrder)));
                }
                else
                {
                    return(new ExifURationalArray(etag, ExifBitConverter.ToURationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 6) // 1 = SBYTE An 8-bit signed integer.
            {
                if (count == 1)
                {
                    return(new ExifSByte(etag, (sbyte)value[0]));
                }
                else
                {
                    sbyte[] data = new sbyte[count];
                    Buffer.BlockCopy(value, 0, data, 0, (int)count);
                    return(new ExifSByteArray(etag, data));
                }
            }
            else if (type == 7) // 7 = UNDEFINED An 8-bit byte that can take any value depending on the field definition.
            {
                return(new ExifUndefined(etag, value));
            }
            else if (type == 8) // 8 = SSHORT A 16-bit (2-byte) signed integer.
            {
                if (count == 1)
                {
                    return(new ExifSShort(etag, conv.ToInt16(value, 0)));
                }
                else
                {
                    return(new ExifSShortArray(etag, ExifBitConverter.ToSShortArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 9) // 9 = SLONG A 32-bit (4-byte) signed integer (2's complement notation).
            {
                if (count == 1)
                {
                    return(new ExifSInt(etag, conv.ToInt32(value, 0)));
                }
                else
                {
                    return(new ExifSIntArray(etag, ExifBitConverter.ToSIntArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 10) // 10 = SRATIONAL Two SLONGs. The first SLONG is the numerator and the second SLONG is the denominator.
            {
                if (count == 1)
                {
                    return(new ExifSRational(etag, ExifBitConverter.ToSRational(value, byteOrder)));
                }
                else
                {
                    return(new ExifSRationalArray(etag, ExifBitConverter.ToSRationalArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 11) // 11 = FLOAT Single precision (4-byte) IEEE format.
            {
                if (count == 1)
                {
                    return(new ExifFloat(etag, conv.ToSingle(value, 0)));
                }
                else
                {
                    return(new ExifFloatArray(etag, ExifBitConverter.ToSingleArray(value, (int)count, byteOrder)));
                }
            }
            else if (type == 12) // 12 = DOUBLE Double precision (8-byte) IEEE format.
            {
                if (count == 1)
                {
                    return(new ExifDouble(etag, conv.ToDouble(value, 0)));
                }
                else
                {
                    return(new ExifDoubleArray(etag, ExifBitConverter.ToDoubleArray(value, (int)count, byteOrder)));
                }
            }
            else
            {
                throw new ArgumentException("Unknown property type.");
            }
        }
 public ExifProperty(ExifTag tag)
 {
     mTag = tag;
     mIFD = ExifTagFactory.GetTagIFD(tag);
 }
 /// <summary>
 /// Creates an ExifProperty from the given interoperability parameters.
 /// </summary>
 /// <param name="interOperability">Property data.</param>
 /// <param name="byteOrder">Byte order of the source data.</param>
 /// <param name="ifd">IFD section containing this propery.</param>
 /// <returns>an ExifProperty initialized from the interoperability parameters.</returns>
 public static ExifProperty Get(ExifInterOperability interOperability, BitConverterEx.ByteOrder byteOrder, IFD ifd)
 {
     return Get(interOperability.TagID, interOperability.TypeID, interOperability.Count, interOperability.Data, byteOrder, ifd);
 }
Exemple #34
0
 /// <summary>
 /// 返回ExifTag对应于给定的标签ID
 /// </summary>
 public static ExifTag GetExifTag(IFD ifd, ushort tagid)
 {
     return((ExifTag)(ifd + tagid));
 }
Exemple #35
0
 public IFDAttribute(IFD ifd)
 {
     IFD = ifd;
 }
Exemple #36
0
      /// <summary>
      /// 返回对应于给定ExifTag的标签ID
      /// </summary>
      public static ushort GetTagID(ExifTag exiftag)
      {
          IFD ifd = GetTagIFD(exiftag);

          return((ushort)((int)exiftag - (int)ifd));
      }
Exemple #37
0
        private void WriteIFD(MemoryStream stream, Dictionary <ExifTag, ExifProperty> ifd, IFD ifdtype, long tiffoffset, bool preserveMakerNote)
        {
            BitConverterEx conv = new BitConverterEx(BitConverterEx.ByteOrder.System, ByteOrder);

            // Create a queue of fields to write
            Queue <ExifProperty> fieldqueue = new Queue <ExifProperty>();

            foreach (ExifProperty prop in ifd.Values)
            {
                if (prop.Tag != ExifTag.MakerNote)
                {
                    fieldqueue.Enqueue(prop);
                }
            }
            // Push the maker note data to the end
            if (ifd.ContainsKey(ExifTag.MakerNote))
            {
                fieldqueue.Enqueue(ifd[ExifTag.MakerNote]);
            }

            // Offset to start of field data from start of TIFF header
            uint dataoffset         = (uint)(2 + ifd.Count * 12 + 4 + stream.Position - tiffoffset);
            uint currentdataoffset  = dataoffset;
            long absolutedataoffset = stream.Position + (2 + ifd.Count * 12 + 4);

            bool makernotewritten = false;

            // Field count
            stream.Write(conv.GetBytes((ushort)ifd.Count), 0, 2);
            // Fields
            while (fieldqueue.Count != 0)
            {
                ExifProperty         field   = fieldqueue.Dequeue();
                ExifInterOperability interop = field.Interoperability;

                uint fillerbytecount = 0;

                // Try to preserve the makernote data offset
                if (!makernotewritten &&
                    !makerNoteProcessed &&
                    makerNoteOffset != 0 &&
                    ifdtype == IFD.EXIF &&
                    field.Tag != ExifTag.MakerNote &&
                    interop.Data.Length > 4 &&
                    currentdataoffset + interop.Data.Length > makerNoteOffset &&
                    ifd.ContainsKey(ExifTag.MakerNote))
                {
                    // Delay writing this field until we write makernote data
                    fieldqueue.Enqueue(field);
                    continue;
                }
                else if (field.Tag == ExifTag.MakerNote)
                {
                    makernotewritten = true;
                    // We may need to write filler bytes to preserve maker note offset
                    if (preserveMakerNote && !makerNoteProcessed)
                    {
                        fillerbytecount = makerNoteOffset - currentdataoffset;
                    }
                    else
                    {
                        fillerbytecount = 0;
                    }
                }

                // Tag
                stream.Write(conv.GetBytes(interop.TagID), 0, 2);
                // Type
                stream.Write(conv.GetBytes(interop.TypeID), 0, 2);
                // Count
                stream.Write(conv.GetBytes(interop.Count), 0, 4);
                // Field data
                byte[] data = interop.Data;
                if (ByteOrder != BitConverterEx.SystemByteOrder)
                {
                    if (interop.TypeID == 1 || interop.TypeID == 3 || interop.TypeID == 4 || interop.TypeID == 9)
                    {
                        Array.Reverse(data);
                    }
                    else if (interop.TypeID == 5 || interop.TypeID == 10)
                    {
                        Array.Reverse(data, 0, 4);
                        Array.Reverse(data, 4, 4);
                    }
                }

                // Fields containing offsets to other IFDs
                // Just store their offets, we will write the values later on when we know the lengths of IFDs
                if (ifdtype == IFD.Zeroth && interop.TagID == 0x8769)
                {
                    exifIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.Zeroth && interop.TagID == 0x8825)
                {
                    gpsIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.EXIF && interop.TagID == 0xa005)
                {
                    interopIFDFieldOffset = stream.Position;
                }
                else if (ifdtype == IFD.First && interop.TagID == 0x201)
                {
                    thumbOffsetLocation = stream.Position;
                }
                else if (ifdtype == IFD.First && interop.TagID == 0x202)
                {
                    thumbSizeLocation = stream.Position;
                }

                // Write 4 byte field value or field data
                if (data.Length <= 4)
                {
                    stream.Write(data, 0, data.Length);
                    for (int i = data.Length; i < 4; i++)
                    {
                        stream.WriteByte(0);
                    }
                }
                else
                {
                    // Pointer to data area relative to TIFF header
                    stream.Write(conv.GetBytes(currentdataoffset + fillerbytecount), 0, 4);
                    // Actual data
                    long currentoffset = stream.Position;
                    stream.Seek(absolutedataoffset, SeekOrigin.Begin);
                    // Write filler bytes
                    for (int i = 0; i < fillerbytecount; i++)
                    {
                        stream.WriteByte(0xFF);
                    }
                    stream.Write(data, 0, data.Length);
                    stream.Seek(currentoffset, SeekOrigin.Begin);
                    // Increment pointers
                    currentdataoffset  += fillerbytecount + (uint)data.Length;
                    absolutedataoffset += fillerbytecount + data.Length;
                }
            }
            // Offset to 1st IFD
            // We will write zeros for now. This will be filled after we write all IFDs
            if (ifdtype == IFD.Zeroth)
            {
                firstIFDFieldOffset = stream.Position;
            }
            stream.Write(new byte[] { 0, 0, 0, 0 }, 0, 4);

            // Seek to end of IFD
            stream.Seek(absolutedataoffset, SeekOrigin.Begin);

            // Write thumbnail data
            if (ifdtype == IFD.First)
            {
                if (Thumbnail != null)
                {
                    MemoryStream ts = new MemoryStream();
                    Thumbnail.Save(ts);
                    ts.Close();
                    byte[] thumb = ts.ToArray();
                    thumbOffsetValue = (uint)(stream.Position - tiffoffset);
                    thumbSizeValue   = (uint)thumb.Length;
                    stream.Write(thumb, 0, thumb.Length);
                    ts.Dispose();
                }
                else
                {
                    thumbOffsetValue = 0;
                    thumbSizeValue   = 0;
                }
            }
        }
Exemple #38
0
 /// <summary>
 /// 返回给定标签ID字符串表示
 /// </summary>
 public static string GetTagName(IFD ifd, ushort tagid)
 {
     return(GetTagName(GetExifTag(ifd, tagid)));
 }