Changed GetDimensions to handle a stream at the end and one exit Switched to using Action? over IDlibDotNet for Tick method Switched to using AsReadOnly over new() Moved Meta Base to Shared
143 lines
4.9 KiB
C#
143 lines
4.9 KiB
C#
using System.Drawing;
|
|
|
|
namespace View_by_Distance.Metadata.Models.Stateless.Methods;
|
|
|
|
internal static class Dimensions
|
|
{
|
|
|
|
#pragma warning disable IDE0230
|
|
private static readonly Dictionary<byte[], Func<BinaryReader, Size?>> _ImageFormatDecoders = new()
|
|
{
|
|
{ new byte[] { 0xff, 0xd8 }, DecodeJfif },
|
|
{ new byte[] { 0x42, 0x4D }, DecodeBitmap },
|
|
{ new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP },
|
|
{ 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 },
|
|
};
|
|
#pragma warning restore IDE0230
|
|
|
|
private static bool StartsWith(List<byte> thisBytes, byte[] thatBytes)
|
|
{
|
|
for (int i = 0; i < thisBytes.Count && i < thatBytes.Length; i += 1)
|
|
{
|
|
if (thisBytes[i] == thatBytes[i])
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static short ReadLittleEndianInt16(BinaryReader binaryReader)
|
|
{
|
|
byte[] 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);
|
|
}
|
|
|
|
private static int ReadLittleEndianInt32(BinaryReader binaryReader)
|
|
{
|
|
byte[] 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);
|
|
}
|
|
|
|
private static Size? DecodeBitmap(BinaryReader binaryReader)
|
|
{
|
|
_ = binaryReader.ReadBytes(16);
|
|
int width = binaryReader.ReadInt32();
|
|
int height = binaryReader.ReadInt32();
|
|
return new Size(width, height);
|
|
}
|
|
|
|
private static Size? DecodeGif(BinaryReader binaryReader)
|
|
{
|
|
int width = binaryReader.ReadInt16();
|
|
int height = binaryReader.ReadInt16();
|
|
return new Size(width, height);
|
|
}
|
|
|
|
private static Size? DecodePng(BinaryReader binaryReader)
|
|
{
|
|
_ = binaryReader.ReadBytes(8);
|
|
int width = ReadLittleEndianInt32(binaryReader);
|
|
int height = ReadLittleEndianInt32(binaryReader);
|
|
return new Size(width, height);
|
|
}
|
|
|
|
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)
|
|
_ = binaryReader.ReadBytes(chunkLength - 2);
|
|
else
|
|
{
|
|
ushort uChunkLength = (ushort)chunkLength;
|
|
_ = binaryReader.ReadBytes(uChunkLength - 2);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static Size? DecodeWebP(BinaryReader binaryReader)
|
|
{
|
|
_ = binaryReader.ReadUInt32(); // Size
|
|
_ = binaryReader.ReadBytes(15); // WEBP, VP8 + more
|
|
_ = binaryReader.ReadBytes(3); // SYNC
|
|
int width = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits width
|
|
int height = binaryReader.ReadUInt16() & 0b00_11111111111111; // 14 bits height
|
|
return new Size(width, height);
|
|
}
|
|
|
|
internal static Size? GetDimensions(BinaryReader binaryReader)
|
|
{
|
|
Size? result;
|
|
List<byte> magicBytes = [];
|
|
int[] magicBytesLengths = (from l in _ImageFormatDecoders.Keys where l.Length <= binaryReader.BaseStream.Length orderby l.Length descending select l.Length).ToArray();
|
|
if (magicBytesLengths.Length == 0)
|
|
result = null;
|
|
else
|
|
{
|
|
result = null;
|
|
if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position)
|
|
_ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin);
|
|
for (int i = 0; i < magicBytesLengths[0]; i++)
|
|
{
|
|
magicBytes.Add(binaryReader.ReadByte());
|
|
foreach (KeyValuePair<byte[], Func<BinaryReader, Size?>> kvPair in _ImageFormatDecoders)
|
|
{
|
|
if (StartsWith(magicBytes, kvPair.Key))
|
|
{
|
|
result = kvPair.Value(binaryReader);
|
|
break;
|
|
}
|
|
}
|
|
if (result is not null)
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
internal static Size? GetDimensions(string path)
|
|
{
|
|
Size? result;
|
|
using FileStream fileStream = File.OpenRead(path);
|
|
using BinaryReader binaryReader = new(fileStream);
|
|
result = GetDimensions(binaryReader);
|
|
return result;
|
|
}
|
|
|
|
} |