We are using .NET 7 web MVC application and SkiaSharp 2.88.5. We have LoadImage controller action which receives imageId and scaleFactor parameters and scales the image according to the scaling factor. The image can be either in JPG or PNG format.
public IActionResult LoadImage(int imageId, double scaleFactor)
{
var path = _imageService.GetImagePath(imageId);
if (System.IO.File.Exists(path))
{
var extension = Path.GetExtension(path);
var contentType = FileTypeMapping.GetMimeTypeFromExtension(extension);
var img = System.IO.File.OpenRead(path);
var resizedImage = FileHelpers.ResizeImage(img, extension, scaleFactor);
return File(resizedImage, contentType);
}
return NotFound();
}
The following method performs resizing operation using SkiaSharp library:
private static readonly Dictionary<string, SKEncodedImageFormat> _skiaSharpImageFormatMapping = new(StringComparer.InvariantCultureIgnoreCase)
{
{".png", SKEncodedImageFormat.Png },
{".jpg", SKEncodedImageFormat.Jpeg },
{".jpeg", SKEncodedImageFormat.Jpeg },
{".jpe", SKEncodedImageFormat.Jpeg },
};
public static Stream ResizeImage(Stream imgStream, string extension, double scaleFactor)
{
using (var skData = SKData.Create(imgStream))
{
using (var skImage = SKBitmap.Decode(skData))
{
int newHeight = (int)(skImage.Height * scaleFactor);
int newWidth = (int)(skImage.Width * scaleFactor);
using (var scaledBitmap = skImage.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.Low))
{
using (var image = SKImage.FromBitmap(scaledBitmap))
{
using (var encodedImage = image.Encode(_skiaSharpImageFormatMapping[extension], 50))
{
var stream = new MemoryStream();
encodedImage.SaveTo(stream);
stream.Seek(0, SeekOrigin.Begin);
return stream;
}
}
}
}
}
}
The image that we are trying to resize is a JPG image of size 2963KB (4032x3025px). When we call /LoadImage?imageId=555&scaleFactor=0.5 endpoint, we can observe the following increase in process memory:
The memory increased from 385MB to 471MB, which is an increase of 86MB. The increase seems quite high, especially considering the fact that the image is only 2.9MB in size. It becomes problematic when multiple clients call LoadImage action at the same time. The server can quickly run out of memory.
Is such memory increase expected? How can we make it more memory-efficient? Is there something wrong with the resizing algorithm (ResizeImage method) that we use which causes before-mentioned problem?
