/// <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> /// 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> /// Selects the specified bin. /// </summary> public void SelectBin(Bin bin) { long offset = mMap.Position; mMap.Seek(0, System.IO.SeekOrigin.Begin); while (!mMap.EOF) { Bin s = mMap.Read(); if (s.GetHashCode() == bin.GetHashCode()) { mMap.Seek(s.Offset, System.IO.SeekOrigin.Begin); selectedbin = s.GetHashCode(); Refresh(); return; } } mMap.Seek(offset, System.IO.SeekOrigin.Begin); return; }