diff options
| author | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-21 01:02:10 -0500 |
|---|---|---|
| committer | LukePulverenti <luke.pulverenti@gmail.com> | 2013-02-21 01:02:10 -0500 |
| commit | ae4ffa75be378497d8db210c6864cde40f7c75f9 (patch) | |
| tree | 14d7a0610ff5117f3ea92ddaee71c219bbbc4865 /MediaBrowser.Controller/Drawing/ImageHeader.cs | |
| parent | acf5b0b6ed173a3a9540d0585bd491a119d524cf (diff) | |
isolated weather and moved drawing classes up to the controller project
Diffstat (limited to 'MediaBrowser.Controller/Drawing/ImageHeader.cs')
| -rw-r--r-- | MediaBrowser.Controller/Drawing/ImageHeader.cs | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Drawing/ImageHeader.cs b/MediaBrowser.Controller/Drawing/ImageHeader.cs new file mode 100644 index 000000000..7cc63eee9 --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageHeader.cs @@ -0,0 +1,216 @@ +using MediaBrowser.Common.Logging; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Controller.Drawing +{ + /// <summary> + /// Taken from http://stackoverflow.com/questions/111345/getting-image-dimensions-without-reading-the-entire-file/111349 + /// http://www.codeproject.com/Articles/35978/Reading-Image-Headers-to-Get-Width-and-Height + /// Minor improvements including supporting unsigned 16-bit integers when decoding Jfif and added logic + /// to load the image using new Bitmap if reading the headers fails + /// </summary> + public static class ImageHeader + { + /// <summary> + /// The error message + /// </summary> + const string errorMessage = "Could not recognize image format."; + + /// <summary> + /// The image format decoders + /// </summary> + private static readonly Dictionary<byte[], Func<BinaryReader, Size>> imageFormatDecoders = new Dictionary<byte[], Func<BinaryReader, Size>> + { + { new byte[] { 0x42, 0x4D }, DecodeBitmap }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 }, DecodeGif }, + { new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 }, DecodeGif }, + { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }, DecodePng }, + { new byte[] { 0xff, 0xd8 }, DecodeJfif }, + }; + + /// <summary> + /// Gets the dimensions of an image. + /// </summary> + /// <param name="path">The path of the image to get the dimensions of.</param> + /// <returns>The dimensions of the specified image.</returns> + /// <exception cref="ArgumentException">The image was of an unrecognised format.</exception> + public static Size GetDimensions(string path) + { + try + { + using (var fs = File.OpenRead(path)) + { + using (var binaryReader = new BinaryReader(fs)) + { + return GetDimensions(binaryReader); + } + } + } + catch + { + Logger.LogInfo("Failed to read image header for {0}. Doing it the slow way.", path); + + using (var fs = File.OpenRead(path)) + { + // Co it the old fashioned way + using (var b = Image.FromStream(fs, true, false)) + { + return b.Size; + } + } + } + } + + /// <summary> + /// Gets the dimensions of an image. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>Size.</returns> + /// <exception cref="System.ArgumentException">binaryReader</exception> + /// <exception cref="ArgumentException">The image was of an unrecognized format.</exception> + private static Size GetDimensions(BinaryReader binaryReader) + { + int maxMagicBytesLength = imageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; + var magicBytes = new byte[maxMagicBytesLength]; + for (int i = 0; i < maxMagicBytesLength; i += 1) + { + magicBytes[i] = binaryReader.ReadByte(); + foreach (var kvPair in imageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + return kvPair.Value(binaryReader); + } + } + } + + throw new ArgumentException(errorMessage, "binaryReader"); + } + + /// <summary> + /// Startses the with. + /// </summary> + /// <param name="thisBytes">The this bytes.</param> + /// <param name="thatBytes">The that bytes.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> + private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + { + for (int i = 0; i < thatBytes.Length; i += 1) + { + if (thisBytes[i] != thatBytes[i]) + { + return false; + } + } + + return true; + } + + /// <summary> + /// Reads the little endian int16. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>System.Int16.</returns> + private static short ReadLittleEndianInt16(BinaryReader binaryReader) + { + var bytes = new byte[sizeof(short)]; + + for (int i = 0; i < sizeof(short); i += 1) + { + bytes[sizeof(short) - 1 - i] = binaryReader.ReadByte(); + } + return BitConverter.ToInt16(bytes, 0); + } + + /// <summary> + /// Reads the little endian int32. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>System.Int32.</returns> + private static int ReadLittleEndianInt32(BinaryReader binaryReader) + { + var bytes = new byte[sizeof(int)]; + for (int i = 0; i < sizeof(int); i += 1) + { + bytes[sizeof(int) - 1 - i] = binaryReader.ReadByte(); + } + return BitConverter.ToInt32(bytes, 0); + } + + /// <summary> + /// Decodes the bitmap. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>Size.</returns> + private static Size DecodeBitmap(BinaryReader binaryReader) + { + binaryReader.ReadBytes(16); + int width = binaryReader.ReadInt32(); + int height = binaryReader.ReadInt32(); + return new Size(width, height); + } + + /// <summary> + /// Decodes the GIF. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>Size.</returns> + private static Size DecodeGif(BinaryReader binaryReader) + { + int width = binaryReader.ReadInt16(); + int height = binaryReader.ReadInt16(); + return new Size(width, height); + } + + /// <summary> + /// Decodes the PNG. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>Size.</returns> + private static Size DecodePng(BinaryReader binaryReader) + { + binaryReader.ReadBytes(8); + int width = ReadLittleEndianInt32(binaryReader); + int height = ReadLittleEndianInt32(binaryReader); + return new Size(width, height); + } + + /// <summary> + /// Decodes the jfif. + /// </summary> + /// <param name="binaryReader">The binary reader.</param> + /// <returns>Size.</returns> + /// <exception cref="System.ArgumentException"></exception> + private static Size DecodeJfif(BinaryReader binaryReader) + { + while (binaryReader.ReadByte() == 0xff) + { + byte marker = binaryReader.ReadByte(); + short chunkLength = ReadLittleEndianInt16(binaryReader); + if (marker == 0xc0) + { + binaryReader.ReadByte(); + int height = ReadLittleEndianInt16(binaryReader); + int width = ReadLittleEndianInt16(binaryReader); + return new Size(width, height); + } + + if (chunkLength < 0) + { + var uchunkLength = (ushort)chunkLength; + binaryReader.ReadBytes(uchunkLength - 2); + } + else + { + binaryReader.ReadBytes(chunkLength - 2); + } + } + + throw new ArgumentException(errorMessage); + } + } +} |
