Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. using System;
  2. using System.IO;
  3. namespace ExifLib
  4. {
  5. /// <summary>
  6. /// Based on http://www.media.mit.edu/pia/Research/deepview/exif.html
  7. /// http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
  8. /// </summary>
  9. public class ExifReader
  10. {
  11. public JpegInfo info { get; private set; }
  12. private bool littleEndian = false;
  13. public static JpegInfo ReadJpeg(byte[] fiBYTES, string Name)
  14. {
  15. DateTime then = DateTime.Now;
  16. using (MemoryStream fs = new MemoryStream(fiBYTES))
  17. {
  18. ExifReader reader = new ExifReader(fs);
  19. reader.info.FileSize = (int)fs.Length;
  20. reader.info.FileName = Name;
  21. reader.info.LoadTime = (DateTime.Now - then);
  22. return reader.info;
  23. }
  24. }
  25. protected ExifReader(Stream stream)
  26. {
  27. info = new JpegInfo();
  28. int a = stream.ReadByte();
  29. // ensure SOI marker
  30. if (a != JpegId.START || stream.ReadByte() != JpegId.SOI)
  31. return;
  32. info.IsValid = true;
  33. for (; ; )
  34. {
  35. int marker = 0, prev = 0;
  36. // find next marker
  37. for (a = 0; ; ++a)
  38. {
  39. marker = stream.ReadByte();
  40. if (marker != JpegId.START && prev == JpegId.START) break;
  41. prev = marker;
  42. }
  43. // read section length
  44. int lenHigh = stream.ReadByte();
  45. int lenLow = stream.ReadByte();
  46. int itemlen = (lenHigh << 8) | lenLow;
  47. // read the section
  48. byte[] section = new byte[itemlen];
  49. section[0] = (byte)lenHigh;
  50. section[1] = (byte)lenLow;
  51. int bytesRead = stream.Read(section, 2, itemlen - 2);
  52. if (bytesRead != itemlen - 2)
  53. return;
  54. switch (marker)
  55. {
  56. case JpegId.SOS:
  57. // start of stream: and we're done
  58. return;
  59. case JpegId.EOI:
  60. // no data? no good.
  61. return;
  62. case JpegId.EXIF:
  63. {
  64. if (section[2] == 'E' &&
  65. section[3] == 'x' &&
  66. section[4] == 'i' &&
  67. section[5] == 'f')
  68. {
  69. ProcessExif(section);
  70. }
  71. } break;
  72. case JpegId.IPTC:
  73. {
  74. // don't care.
  75. } break;
  76. case 0xC0:
  77. case 0xC1:
  78. case 0xC2:
  79. case 0xC3:
  80. // case 0xC4: // not SOF
  81. case 0xC5:
  82. case 0xC6:
  83. case 0xC7:
  84. // case 0xC8: // not SOF
  85. case 0xC9:
  86. case 0xCA:
  87. case 0xCB:
  88. // case 0xCC: // not SOF
  89. case 0xCD:
  90. case 0xCE:
  91. case 0xCF:
  92. {
  93. ProcessSOF(section, marker);
  94. } break;
  95. default:
  96. {
  97. // don't care.
  98. } break;
  99. }
  100. section = null;
  101. GC.Collect();
  102. }
  103. }
  104. private void ProcessExif(byte[] section)
  105. {
  106. int idx = 6;
  107. if (section[idx++] != 0 ||
  108. section[idx++] != 0)
  109. {
  110. // "Exif" is not followed by 2 null bytes.
  111. return;
  112. }
  113. if (section[idx] == 'I' && section[idx + 1] == 'I')
  114. {
  115. // intel order
  116. littleEndian = true;
  117. }
  118. else
  119. {
  120. if (section[idx] == 'M' && section[idx + 1] == 'M')
  121. littleEndian = false;
  122. else
  123. {
  124. // unknown order...
  125. return;
  126. }
  127. }
  128. idx += 2;
  129. int a = ExifIO.ReadUShort(section, idx, littleEndian);
  130. idx += 2;
  131. if (a != 0x002A)
  132. {
  133. // bad start...
  134. return;
  135. }
  136. a = ExifIO.ReadInt(section, idx, littleEndian);
  137. idx += 4;
  138. if (a < 8 || a > 16)
  139. {
  140. if (a < 16 || a > section.Length - 16)
  141. {
  142. // invalid offset
  143. return;
  144. }
  145. }
  146. ProcessExifDir(section, a + 8, 8, section.Length - 8, 0, ExifIFD.Exif);
  147. }
  148. private int DirOffset(int start, int num)
  149. {
  150. return start + 2 + 12 * num;
  151. }
  152. private void ProcessExifDir(byte[] section, int offsetDir, int offsetBase, int length, int depth, ExifIFD ifd)
  153. {
  154. if (depth > 4)
  155. {
  156. // corrupted Exif header...
  157. return;
  158. }
  159. ushort numEntries = ExifIO.ReadUShort(section, offsetDir, littleEndian);
  160. if (offsetDir + 2 + 12 * numEntries >= offsetDir + length)
  161. {
  162. // too long
  163. return;
  164. }
  165. int offset = 0;
  166. for (int de = 0; de < numEntries; ++de)
  167. {
  168. offset = DirOffset(offsetDir, de);
  169. ExifTag exifTag = new ExifTag(section, offset, offsetBase, length, littleEndian);
  170. if (!exifTag.IsValid)
  171. continue;
  172. switch (exifTag.Tag)
  173. {
  174. case (int)ExifIFD.Exif:
  175. {
  176. int dirStart = offsetBase + exifTag.GetInt(0);
  177. if (dirStart <= offsetBase + length)
  178. {
  179. ProcessExifDir(section, dirStart, offsetBase, length, depth + 1, ExifIFD.Exif);
  180. }
  181. } break;
  182. case (int)ExifIFD.Gps:
  183. {
  184. int dirStart = offsetBase + exifTag.GetInt(0);
  185. if (dirStart <= offsetBase + length)
  186. {
  187. ProcessExifDir(section, dirStart, offsetBase, length, depth + 1, ExifIFD.Gps);
  188. }
  189. } break;
  190. default:
  191. {
  192. exifTag.Populate(info, ifd);
  193. } break;
  194. }
  195. }
  196. // final link defined?
  197. offset = DirOffset(offsetDir, numEntries) + 4;
  198. if (offset <= offsetBase + length)
  199. {
  200. offset = ExifIO.ReadInt(section, offsetDir + 2 + 12 * numEntries, littleEndian);
  201. if (offset > 0)
  202. {
  203. int subDirStart = offsetBase + offset;
  204. if (subDirStart <= offsetBase + length && subDirStart >= offsetBase)
  205. {
  206. ProcessExifDir(section, subDirStart, offsetBase, length, depth + 1, ifd);
  207. }
  208. }
  209. }
  210. if (info.ThumbnailData == null && info.ThumbnailOffset > 0 && info.ThumbnailSize > 0)
  211. {
  212. // store it.
  213. info.ThumbnailData = new byte[info.ThumbnailSize];
  214. Array.Copy(section, offsetBase + info.ThumbnailOffset, info.ThumbnailData, 0, info.ThumbnailSize);
  215. }
  216. }
  217. private void ProcessSOF(byte[] section, int marker)
  218. {
  219. // bytes 1,2 is section len
  220. // byte 3 is precision (bytes per sample)
  221. info.Height = ((int)section[3] << 8) | (int)section[4];
  222. info.Width = ((int)section[5] << 8) | (int)section[6];
  223. int components = (int)section[7];
  224. info.IsColor = (components == 3);
  225. }
  226. }
  227. }