/// <summary> /// Initializes a new instance of the <see cref="JPEGFile"/> class. /// </summary> /// <param name="stream">A stream that contains image data.</param> /// <param name="encoding">The encoding to be used for text metadata when the source encoding is unknown.</param> /// <param name="readTrailingData">Whether to read data beyond the EOI (end of image) marker.</param> protected internal JPEGFile(MemoryStream stream, Encoding encoding, bool readTrailingData = false) { Format = ImageFileFormat.JPEG; Sections = new List <JPEGSection>(); TrailingData = new byte[0]; Encoding = encoding; stream.Seek(0, SeekOrigin.Begin); // Read the Start of Image (SOI) marker. SOI marker is represented // with two bytes: 0xFF, 0xD8. byte[] markerbytes = new byte[2]; if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8) { throw new NotValidJPEGFileException(); } stream.Seek(0, SeekOrigin.Begin); // Search and read sections until we reach the end of file. while (stream.Position != stream.Length) { // Read the next section marker. Section markers are two bytes // with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF. if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || markerbytes[1] == 0xFF) { throw new NotValidJPEGFileException(); } JPEGMarker marker = (JPEGMarker)markerbytes[1]; byte[] header = new byte[0]; // SOI, EOI and RST markers do not contain any header if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) { // Length of the header including the length bytes. // This value is a 16-bit unsigned integer // in big endian byte-order. byte[] lengthbytes = new byte[2]; if (stream.Read(lengthbytes, 0, 2) != 2) { throw new NotValidJPEGFileException(); } long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0); // Read section header. header = Utility.GetStreamBytes(stream, length - 2); } // Start of Scan (SOS) sections and RST sections are immediately // followed by entropy coded data. For that, we need to read until // the next section marker once we reach a SOS or RST. byte[] entropydata = new byte[0]; if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) { long position = stream.Position; // Search for the next section marker while (true) { // Search for an 0xFF indicating start of a marker int nextbyte = 0; do { nextbyte = stream.ReadByte(); if (nextbyte == -1) { break; } } while ((byte)nextbyte != 0xFF); // Skip filler bytes (0xFF) do { nextbyte = stream.ReadByte(); if (nextbyte == -1) { break; } } while ((byte)nextbyte == 0xFF); // We either reached the end of file before a new marker(this would indicate // a corrupt image file) or we are at a section marker. In that case the // next byte must not be 0x00. if (nextbyte != 0) { // If we reached a section marker seek back to just before the marker. if (nextbyte != -1) { stream.Seek(-2, SeekOrigin.Current); } // Calculate the length of the entropy coded data. long edlength = stream.Position - position; stream.Seek(position, SeekOrigin.Begin); // Read entropy coded data entropydata = Utility.GetStreamBytes(stream, edlength); break; } } } // Store section. JPEGSection section = new JPEGSection(marker, header, entropydata); Sections.Add(section); // Some propriety formats store data past the EOI marker if (marker == JPEGMarker.EOI) { if (readTrailingData) { long eoflength = stream.Length - stream.Position; TrailingData = Utility.GetStreamBytes(stream, eoflength); } // stop reading once we are past the EOI marker break; } } // Read metadata sections ReadJFIFAPP0(); ReadJFXXAPP0(); ReadExifAPP1(); // Process the maker note makerNoteProcessed = false; }
/// <summary> /// Constructs a JPEGSection represented by the marker byte. /// </summary> /// <param name="marker">The marker byte representing the section.</param> public JPEGSection(JPEGMarker marker) : this(marker, new byte[0], new byte[0]) { ; }
/// <summary> /// Initializes a new instance of the <see cref="ExifFile"/> class. /// </summary> /// <param name="stream">A <see cref="Sytem.IO.Stream"/> that contains image data.</param> protected internal JPEGFile(Stream stream) { Format = ImageFileFormat.JPEG; Sections = new List <JPEGSection>(); TrailingData = new byte[0]; stream.Seek(0, SeekOrigin.Begin); // Read the Start of Image (SOI) marker. SOI marker is represented // with two bytes: 0xFF, 0xD8. byte[] markerbytes = new byte[2]; if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] != 0xD8) { throw new NotValidJPEGFileException(); } stream.Seek(0, SeekOrigin.Begin); // Search and read sections until we reach the end of file. while (stream.Position != stream.Length) { // Read the next section marker. Section markers are two bytes // with values 0xFF, 0x?? where ?? must not be 0x00 or 0xFF. if (stream.Read(markerbytes, 0, 2) != 2 || markerbytes[0] != 0xFF || markerbytes[1] == 0x00 || markerbytes[1] == 0xFF) { throw new NotValidJPEGFileException(); } JPEGMarker marker = (JPEGMarker)markerbytes[1]; byte[] header = new byte[0]; // SOI, EOI and RST markers do not contain any header if (marker != JPEGMarker.SOI && marker != JPEGMarker.EOI && !(marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) { // Length of the header including the length bytes. // This value is a 16-bit unsigned integer // in big endian byte-order. byte[] lengthbytes = new byte[2]; if (stream.Read(lengthbytes, 0, 2) != 2) { throw new NotValidJPEGFileException(); } long length = (long)BitConverterEx.BigEndian.ToUInt16(lengthbytes, 0); // Read section header. header = new byte[length - 2]; int bytestoread = header.Length; while (bytestoread > 0) { int count = Math.Min(bytestoread, 4 * 1024); int bytesread = stream.Read(header, header.Length - bytestoread, count); if (bytesread == 0) { throw new NotValidJPEGFileException(); } bytestoread -= bytesread; } } // Start of Scan (SOS) sections and RST sections are immediately // followed by entropy coded data. For that, we need to read until // the next section marker once we reach a SOS or RST. byte[] entropydata = new byte[0]; if (marker == JPEGMarker.SOS || (marker >= JPEGMarker.RST0 && marker <= JPEGMarker.RST7)) { long position = stream.Position; // Search for the next section marker while (true) { // Search for an 0xFF indicating start of a marker int nextbyte = 0; do { nextbyte = stream.ReadByte(); if (nextbyte == -1) { throw new NotValidJPEGFileException(); } } while ((byte)nextbyte != 0xFF); // Skip filler bytes (0xFF) do { nextbyte = stream.ReadByte(); if (nextbyte == -1) { throw new NotValidJPEGFileException(); } } while ((byte)nextbyte == 0xFF); // Looks like a section marker. The next byte must not be 0x00. if ((byte)nextbyte != 0x00) { // We reached a section marker. Calculate the // length of the entropy coded data. stream.Seek(-2, SeekOrigin.Current); long edlength = stream.Position - position; stream.Seek(-edlength, SeekOrigin.Current); // Read entropy coded data entropydata = new byte[edlength]; int bytestoread = entropydata.Length; while (bytestoread > 0) { int count = Math.Min(bytestoread, 4 * 1024); int bytesread = stream.Read(entropydata, entropydata.Length - bytestoread, count); if (bytesread == 0) { throw new NotValidJPEGFileException(); } bytestoread -= bytesread; } break; } } } // Store section. JPEGSection section = new JPEGSection(marker, header, entropydata); Sections.Add(section); // Some propriety formats store data past the EOI marker if (marker == JPEGMarker.EOI) { int bytestoread = (int)(stream.Length - stream.Position); TrailingData = new byte[bytestoread]; while (bytestoread > 0) { int count = (int)Math.Min(bytestoread, 4 * 1024); int bytesread = stream.Read(TrailingData, TrailingData.Length - bytestoread, count); if (bytesread == 0) { throw new NotValidJPEGFileException(); } bytestoread -= bytesread; } } } // Read metadata sections ReadJFIFAPP0(); ReadJFXXAPP0(); ReadExifAPP1(); // Process the maker note makerNoteProcessed = false; }
/// <summary> /// Constructs a JPEGSection represented by the marker byte and containing /// the given data. /// </summary> /// <param name="marker">The marker byte representing the section.</param> /// <param name="data">Section data.</param> /// <param name="entropydata">Entropy coded data.</param> public JPEGSection(JPEGMarker marker, byte[] data, byte[] entropydata) { Marker = marker; Header = data; EntropyData = entropydata; }
/// <summary> /// Constructs a JPEGSection represented by the marker byte. /// </summary> /// <param name="marker">The marker byte representing the section.</param> public JPEGSection(JPEGMarker marker) { Marker = marker; }