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]; } }