From 0ec644327022ffe511e9cf39f7b67ebed645046f Mon Sep 17 00:00:00 2001 From: Mike Phares Date: Sun, 15 Jun 2025 07:22:33 -0700 Subject: [PATCH] Changed GetDimensions to handle a stream at the end and one exit --- Metadata/Models/Stateless/Dimensions.cs | 43 +++-- Shared/Models/FaceFileSystem.cs | 2 +- Shared/Models/Stateless/Methods/Dimensions.cs | 149 ++++++++++++++++++ .../Models/Stateless/Methods/ImageHelper.cs | 132 ---------------- 4 files changed, 180 insertions(+), 146 deletions(-) create mode 100644 Shared/Models/Stateless/Methods/Dimensions.cs delete mode 100644 Shared/Models/Stateless/Methods/ImageHelper.cs diff --git a/Metadata/Models/Stateless/Dimensions.cs b/Metadata/Models/Stateless/Dimensions.cs index fd53715..7421ee2 100644 --- a/Metadata/Models/Stateless/Dimensions.cs +++ b/Metadata/Models/Stateless/Dimensions.cs @@ -8,16 +8,16 @@ internal static class Dimensions #pragma warning disable IDE0230 private static readonly Dictionary> _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 }, - { new byte[] { 0xff, 0xd8 }, DecodeJfif }, - { new byte[] { 0x52, 0x49, 0x46, 0x46 }, DecodeWebP }, }; #pragma warning restore IDE0230 - private static bool StartsWith(byte[] thisBytes, byte[] thatBytes) + private static bool StartsWith(List thisBytes, byte[] thatBytes) { for (int i = 0; i < thatBytes.Length; i += 1) { @@ -103,24 +103,41 @@ internal static class Dimensions internal static Size? GetDimensions(BinaryReader binaryReader) { - int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) + Size? result; + List 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 { - magicBytes[i] = binaryReader.ReadByte(); - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + result = null; + if (binaryReader.BaseStream.Length == binaryReader.BaseStream.Position) + _ = binaryReader.BaseStream.Seek(0, SeekOrigin.Begin); + for (int i = 0; i < magicBytesLengths[0]; i++) { - if (StartsWith(magicBytes, kvPair.Key)) - return kvPair.Value(binaryReader); + magicBytes.Add(binaryReader.ReadByte()); + foreach (KeyValuePair> kvPair in _ImageFormatDecoders) + { + if (StartsWith(magicBytes, kvPair.Key)) + { + result = kvPair.Value(binaryReader); + break; + } + } + if (result is not null) + break; } } - return null; + return result; } internal static Size? GetDimensions(string path) { - using BinaryReader binaryReader = new(File.OpenRead(path)); - return GetDimensions(binaryReader); + Size? result; + using FileStream fileStream = File.OpenRead(path); + using BinaryReader binaryReader = new(fileStream); + result = GetDimensions(binaryReader); + return result; } } \ No newline at end of file diff --git a/Shared/Models/FaceFileSystem.cs b/Shared/Models/FaceFileSystem.cs index 0f529bc..776ab2e 100644 --- a/Shared/Models/FaceFileSystem.cs +++ b/Shared/Models/FaceFileSystem.cs @@ -76,7 +76,7 @@ public class FaceFileSystem : FileSystem, Properties.IFaceFileSystem } else { - System.Drawing.Size size = Stateless.Methods.ImageHelper.GetDimensions(fileInfo.FullName, faceRight, faceBottom); + System.Drawing.Size size = Stateless.Methods.Dimensions.GetDimensions(fileInfo.FullName, faceRight, faceBottom); imageWidth = size.Width; imageHeight = size.Height; } diff --git a/Shared/Models/Stateless/Methods/Dimensions.cs b/Shared/Models/Stateless/Methods/Dimensions.cs new file mode 100644 index 0000000..16f14ff --- /dev/null +++ b/Shared/Models/Stateless/Methods/Dimensions.cs @@ -0,0 +1,149 @@ +using System.Drawing; + +namespace View_by_Distance.Shared.Models.Stateless.Methods; + +internal static class Dimensions +{ + +#pragma warning disable IDE0230 + private static readonly Dictionary> _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 thisBytes, byte[] thatBytes) + { + for (int i = 0; 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 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> 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; + } + +} \ No newline at end of file diff --git a/Shared/Models/Stateless/Methods/ImageHelper.cs b/Shared/Models/Stateless/Methods/ImageHelper.cs deleted file mode 100644 index e4493f2..0000000 --- a/Shared/Models/Stateless/Methods/ImageHelper.cs +++ /dev/null @@ -1,132 +0,0 @@ -using System.Drawing; - -namespace View_by_Distance.Shared.Models.Stateless.Methods; - -internal abstract class ImageHelper -{ - - 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; - } - - 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); - } - _ = binaryReader.ReadBytes(chunkLength - 2); - } - throw new ArgumentException("Could not recognize image format."); - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(BinaryReader binaryReader, int? faceRight, int? faceBottom) - { - Size? result = null; -#pragma warning disable IDE0230 - Dictionary> _ImageFormatDecoders = new() - { - { 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 }, - }; -#pragma warning restore IDE0230 - int maxMagicBytesLength = _ImageFormatDecoders.Keys.OrderByDescending(x => x.Length).First().Length; - byte[] magicBytes = new byte[maxMagicBytesLength]; - for (int i = 0; i < maxMagicBytesLength; i += 1) - { - magicBytes[i] = binaryReader.ReadByte(); - foreach (KeyValuePair> kvPair in _ImageFormatDecoders) - { - if (!StartsWith(magicBytes, kvPair.Key)) - continue; - 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; - } - - /// - /// Gets the dimensions of an image. - /// - /// The path of the image to get the dimensions of. - /// The dimensions of the specified image. - /// The image was of an unrecognized format. - internal static Size GetDimensions(string path, int? faceRight, int? faceBottom) - { - Size result; - using BinaryReader binaryReader = new(File.OpenRead(path)); - result = GetDimensions(binaryReader, faceRight, faceBottom); - return result; - } - -} \ No newline at end of file