124 lines
4.1 KiB
C#
124 lines
4.1 KiB
C#
|
|
namespace GhostImageManager.Services;
|
||
|
|
|
||
|
|
public static class ImageDimensionReader
|
||
|
|
{
|
||
|
|
public static (int Width, int Height) Read(Stream stream)
|
||
|
|
{
|
||
|
|
using var reader = new BinaryReader(stream);
|
||
|
|
var header = reader.ReadBytes(8);
|
||
|
|
|
||
|
|
if (IsJpeg(header))
|
||
|
|
return ReadJpeg(stream, reader);
|
||
|
|
|
||
|
|
if (IsPng(header))
|
||
|
|
return ReadPng(reader);
|
||
|
|
|
||
|
|
if (IsGif(header))
|
||
|
|
return ReadGif(reader);
|
||
|
|
|
||
|
|
if (IsWebP(header))
|
||
|
|
return ReadWebP(stream, reader);
|
||
|
|
|
||
|
|
return (0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static bool IsJpeg(byte[] header) => header[0] == 0xFF && header[1] == 0xD8;
|
||
|
|
private static bool IsPng(byte[] header) => header[0] == 0x89 && header[1] == 0x50 && header[2] == 0x4E && header[3] == 0x47;
|
||
|
|
private static bool IsGif(byte[] header) => header[0] == 0x47 && header[1] == 0x49 && header[2] == 0x46;
|
||
|
|
private static bool IsWebP(byte[] header) => header[0] == 0x52 && header[1] == 0x49 && header[2] == 0x46 && header[3] == 0x46;
|
||
|
|
|
||
|
|
private static (int, int) ReadPng(BinaryReader reader)
|
||
|
|
{
|
||
|
|
reader.BaseStream.Seek(16, SeekOrigin.Begin);
|
||
|
|
var width = ReadBigEndianInt(reader);
|
||
|
|
var height = ReadBigEndianInt(reader);
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static (int, int) ReadGif(BinaryReader reader)
|
||
|
|
{
|
||
|
|
reader.BaseStream.Seek(6, SeekOrigin.Begin);
|
||
|
|
var width = reader.ReadUInt16();
|
||
|
|
var height = reader.ReadUInt16();
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static (int, int) ReadJpeg(Stream stream, BinaryReader reader)
|
||
|
|
{
|
||
|
|
stream.Seek(2, SeekOrigin.Begin);
|
||
|
|
while (stream.Position < stream.Length)
|
||
|
|
{
|
||
|
|
int marker = reader.ReadByte();
|
||
|
|
if (marker != 0xFF) continue;
|
||
|
|
|
||
|
|
int type = reader.ReadByte();
|
||
|
|
if (type == 0xD9 || type == 0xDA) break; // EOI or SOS
|
||
|
|
|
||
|
|
// Skip padding
|
||
|
|
while (type == 0xFF) type = reader.ReadByte();
|
||
|
|
if (type == 0xD9 || type == 0xDA) break;
|
||
|
|
|
||
|
|
if (type == 0xD8 || type == 0x01) continue;
|
||
|
|
|
||
|
|
int length = (reader.ReadByte() << 8) | reader.ReadByte();
|
||
|
|
if (length < 2) break;
|
||
|
|
|
||
|
|
if (type == 0xC0 || type == 0xC1 || type == 0xC2 || type == 0xC3 ||
|
||
|
|
type == 0xC5 || type == 0xC6 || type == 0xC7 ||
|
||
|
|
type == 0xC9 || type == 0xCA || type == 0xCB ||
|
||
|
|
type == 0xCD || type == 0xCE || type == 0xCF)
|
||
|
|
{
|
||
|
|
reader.ReadByte(); // precision
|
||
|
|
int height = (reader.ReadByte() << 8) | reader.ReadByte();
|
||
|
|
int width = (reader.ReadByte() << 8) | reader.ReadByte();
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
stream.Seek(length - 2, SeekOrigin.Current);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static (int, int) ReadWebP(Stream stream, BinaryReader reader)
|
||
|
|
{
|
||
|
|
stream.Seek(12, SeekOrigin.Begin); // Skip RIFF....WEBP
|
||
|
|
var chunk = reader.ReadChars(4);
|
||
|
|
var chunkType = new string(chunk);
|
||
|
|
|
||
|
|
if (chunkType == "VP8 ")
|
||
|
|
{
|
||
|
|
stream.Seek(26, SeekOrigin.Begin);
|
||
|
|
int width = reader.ReadUInt16() & 0x3FFF;
|
||
|
|
int height = reader.ReadUInt16() & 0x3FFF;
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (chunkType == "VP8L")
|
||
|
|
{
|
||
|
|
stream.Seek(21, SeekOrigin.Begin);
|
||
|
|
int data = reader.ReadInt32();
|
||
|
|
int width = (data & 0x3FFF) + 1;
|
||
|
|
int height = ((data >> 14) & 0x3FFF) + 1;
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (chunkType == "VP8X")
|
||
|
|
{
|
||
|
|
stream.Seek(24, SeekOrigin.Begin);
|
||
|
|
var sizeBytes = reader.ReadBytes(6);
|
||
|
|
int width = (sizeBytes[0] | (sizeBytes[1] << 8) | (sizeBytes[2] << 16)) + 1;
|
||
|
|
int height = (sizeBytes[3] | (sizeBytes[4] << 8) | (sizeBytes[5] << 16)) + 1;
|
||
|
|
return (width, height);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
private static int ReadBigEndianInt(BinaryReader reader)
|
||
|
|
{
|
||
|
|
var bytes = reader.ReadBytes(4);
|
||
|
|
return (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3];
|
||
|
|
}
|
||
|
|
}
|