using Serilog; using SkiaSharp; /// /// Helper class to get data from image files /// public class ImageProcessing { /// Memoization dictionary for the GCD function private static Dictionary<(int, int), int> _gcdMemo = new(); /// /// Uses the recursive euclidean method to calcuate the GCD of two integers /// /// the first integer /// the second integer /// /// Thrown when either of the inputs is negative /// /// /// The GCD of the inputs /// private int GCD(int a, int b) { Log.Debug("calculating GCD for a:{a} b:{b}", a, b); if (a < 0) { throw new ArgumentOutOfRangeException("a", "This function can only accept positive integers"); } if (b < 0) { throw new ArgumentOutOfRangeException("b", "This function can only accept positive integers"); } /* We can only run this calculation when a is larger than b */ if (a < b) { return GCD(b, a); } /* Check if we have already calculated these inputs */ if (_gcdMemo.TryGetValue((a, b), out int gcd)) { Log.Debug("Memo hit, returning {gcd}", gcd); return gcd; } /* Otherwise we calculate the GCD and return it */ /* Note that the recursive nature of this function means we may hit a match more often */ Log.Debug("No memo hit, running calculation"); gcd = b == 0 ? a : GCD(b, a % b); _gcdMemo.Add((a, b), gcd); return gcd; } /// /// Where possible calculate the aspect ratio of the image at " /// /// /// The path to an image file /// /// /// Either the string representation of the images aspect ratio /// or /// /// /// This function may return null if /// /// does not point to an image /// The image cannot be handled by SKIA /// The image has no size /// You don't have permission to read the image /// /// public string? GetAspectRatioString(string path) { /* This function uses some clever abstraction to allow callers to only worry about passing a path */ return (GetAspectRatioString(LoadImage(path))); } private SKBitmap? LoadImage(string path) { try { using (var stream = File.OpenRead(path)) { var bitmap = SKBitmap.Decode(stream); return bitmap; } } catch (Exception e) { Log.Error(e, "Error decoding image {path}", path); return null; } } private string? GetAspectRatioString(SKBitmap? bitmap) { if (bitmap == null) { return null; } int gcd = GCD(bitmap.Width, bitmap.Height); int aspectW = bitmap.Width / gcd; int aspectH = bitmap.Height / gcd; return $"{aspectW}:{aspectH}"; } }