123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- using System;
- using System.IO;
-
- namespace ExifLib
- {
- /// <summary>
- /// Based on http://www.media.mit.edu/pia/Research/deepview/exif.html
- /// http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
- /// </summary>
- public class ExifReader
- {
- public JpegInfo info { get; private set; }
- private bool littleEndian = false;
-
- public static JpegInfo ReadJpeg(byte[] fiBYTES, string Name)
- {
- DateTime then = DateTime.Now;
- using (MemoryStream fs = new MemoryStream(fiBYTES))
- {
- ExifReader reader = new ExifReader(fs);
- reader.info.FileSize = (int)fs.Length;
- reader.info.FileName = Name;
- reader.info.LoadTime = (DateTime.Now - then);
- return reader.info;
- }
- }
-
- protected ExifReader(Stream stream)
- {
- info = new JpegInfo();
- int a = stream.ReadByte();
-
- // ensure SOI marker
- if (a != JpegId.START || stream.ReadByte() != JpegId.SOI)
- return;
-
- info.IsValid = true;
-
- for (; ; )
- {
- int marker = 0, prev = 0;
-
- // find next marker
- for (a = 0; ; ++a)
- {
- marker = stream.ReadByte();
- if (marker != JpegId.START && prev == JpegId.START) break;
- prev = marker;
- }
-
- // read section length
- int lenHigh = stream.ReadByte();
- int lenLow = stream.ReadByte();
- int itemlen = (lenHigh << 8) | lenLow;
-
- // read the section
- byte[] section = new byte[itemlen];
- section[0] = (byte)lenHigh;
- section[1] = (byte)lenLow;
- int bytesRead = stream.Read(section, 2, itemlen - 2);
- if (bytesRead != itemlen - 2)
- return;
-
- switch (marker)
- {
- case JpegId.SOS:
- // start of stream: and we're done
- return;
- case JpegId.EOI:
- // no data? no good.
- return;
- case JpegId.EXIF:
- {
- if (section[2] == 'E' &&
- section[3] == 'x' &&
- section[4] == 'i' &&
- section[5] == 'f')
- {
- ProcessExif(section);
- }
- } break;
- case JpegId.IPTC:
- {
- // don't care.
- } break;
- case 0xC0:
- case 0xC1:
- case 0xC2:
- case 0xC3:
- // case 0xC4: // not SOF
- case 0xC5:
- case 0xC6:
- case 0xC7:
- // case 0xC8: // not SOF
- case 0xC9:
- case 0xCA:
- case 0xCB:
- // case 0xCC: // not SOF
- case 0xCD:
- case 0xCE:
- case 0xCF:
- {
- ProcessSOF(section, marker);
- } break;
- default:
- {
- // don't care.
- } break;
- }
-
- section = null;
- GC.Collect();
- }
- }
-
- private void ProcessExif(byte[] section)
- {
- int idx = 6;
- if (section[idx++] != 0 ||
- section[idx++] != 0)
- {
- // "Exif" is not followed by 2 null bytes.
- return;
- }
-
- if (section[idx] == 'I' && section[idx + 1] == 'I')
- {
- // intel order
- littleEndian = true;
- }
- else
- {
- if (section[idx] == 'M' && section[idx + 1] == 'M')
- littleEndian = false;
- else
- {
- // unknown order...
- return;
- }
- }
- idx += 2;
-
- int a = ExifIO.ReadUShort(section, idx, littleEndian);
- idx += 2;
-
- if (a != 0x002A)
- {
- // bad start...
- return;
- }
-
- a = ExifIO.ReadInt(section, idx, littleEndian);
- idx += 4;
-
- if (a < 8 || a > 16)
- {
- if (a < 16 || a > section.Length - 16)
- {
- // invalid offset
- return;
- }
- }
-
- ProcessExifDir(section, a + 8, 8, section.Length - 8, 0, ExifIFD.Exif);
- }
-
- private int DirOffset(int start, int num)
- {
- return start + 2 + 12 * num;
- }
-
- private void ProcessExifDir(byte[] section, int offsetDir, int offsetBase, int length, int depth, ExifIFD ifd)
- {
- if (depth > 4)
- {
- // corrupted Exif header...
- return;
- }
-
- ushort numEntries = ExifIO.ReadUShort(section, offsetDir, littleEndian);
- if (offsetDir + 2 + 12 * numEntries >= offsetDir + length)
- {
- // too long
- return;
- }
-
- int offset = 0;
-
- for (int de = 0; de < numEntries; ++de)
- {
- offset = DirOffset(offsetDir, de);
- ExifTag exifTag = new ExifTag(section, offset, offsetBase, length, littleEndian);
-
- if (!exifTag.IsValid)
- continue;
-
- switch (exifTag.Tag)
- {
- case (int)ExifIFD.Exif:
- {
- int dirStart = offsetBase + exifTag.GetInt(0);
- if (dirStart <= offsetBase + length)
- {
- ProcessExifDir(section, dirStart, offsetBase, length, depth + 1, ExifIFD.Exif);
- }
- } break;
- case (int)ExifIFD.Gps:
- {
- int dirStart = offsetBase + exifTag.GetInt(0);
- if (dirStart <= offsetBase + length)
- {
- ProcessExifDir(section, dirStart, offsetBase, length, depth + 1, ExifIFD.Gps);
- }
- } break;
- default:
- {
- exifTag.Populate(info, ifd);
- } break;
- }
- }
-
- // final link defined?
- offset = DirOffset(offsetDir, numEntries) + 4;
- if (offset <= offsetBase + length)
- {
- offset = ExifIO.ReadInt(section, offsetDir + 2 + 12 * numEntries, littleEndian);
- if (offset > 0)
- {
- int subDirStart = offsetBase + offset;
- if (subDirStart <= offsetBase + length && subDirStart >= offsetBase)
- {
- ProcessExifDir(section, subDirStart, offsetBase, length, depth + 1, ifd);
- }
- }
- }
-
- if (info.ThumbnailData == null && info.ThumbnailOffset > 0 && info.ThumbnailSize > 0)
- {
- // store it.
- info.ThumbnailData = new byte[info.ThumbnailSize];
- Array.Copy(section, offsetBase + info.ThumbnailOffset, info.ThumbnailData, 0, info.ThumbnailSize);
- }
- }
-
- private void ProcessSOF(byte[] section, int marker)
- {
- // bytes 1,2 is section len
- // byte 3 is precision (bytes per sample)
- info.Height = ((int)section[3] << 8) | (int)section[4];
- info.Width = ((int)section[5] << 8) | (int)section[6];
- int components = (int)section[7];
-
- info.IsColor = (components == 3);
- }
- }
- }
|