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