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
149 lines
5.2 KiB
C#
149 lines
5.2 KiB
C#
using System.Drawing;
|
|
|
|
namespace View_by_Distance.Shared.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, int? faceRight, int? faceBottom)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
if (result is null)
|
|
{
|
|
if (faceRight is null || faceBottom is null)
|
|
throw new Exception("face is null!");
|
|
result = new(faceRight.Value, faceBottom.Value);
|
|
}
|
|
return result.Value;
|
|
}
|
|
|
|
internal static Size GetDimensions(string path, int? faceRight, int? faceBottom)
|
|
{
|
|
Size result;
|
|
using FileStream fileStream = File.OpenRead(path);
|
|
using BinaryReader binaryReader = new(fileStream);
|
|
result = GetDimensions(binaryReader, faceRight, faceBottom);
|
|
return result;
|
|
}
|
|
|
|
} |