I'm experimenting with different ways of rendering image in a browser. The idea is to create custom HTML element capable of displaying a set of images or frames as a continuous animation. The images will be generated on the server side and streamed to the browser client. The easiest way seems to be using img
tag consistently updates its src
attribute. For this, I subscribe to onload
event and once image is loaded, I update image URL with random timestamp and it automatically sends new request creating infinite loop that looks like an animation. The solution is pretty simple and working, but affects CPU performance and doesn't use any GPU acceleration.
Controller used as an image source
[ApiController]
public class StreamController : ControllerBase
{
[Route("/source")]
public ActionResult Get()
{
var o = new Random();
var pos = o.Next(50, 150);
var map = new SKBitmap(250, 250);
var canvas = new SKCanvas(map);
var paint = new SKPaint
{
Color = SKColors.Black,
Style = SKPaintStyle.Fill,
FilterQuality = SKFilterQuality.High
};
canvas.DrawCircle(pos, pos, 20, paint);
var image = SKImage.FromBitmap(map);
var ms = new MemoryStream();
image.Encode(SKEncodedImageFormat.Png, 100).SaveTo(ms);
ms.Position = 0;
return new FileStreamResult(ms, "image/png");
}
}
HTML page
@page "/"
<!-- Solution #1 : SRC attribute -->
<img
width="250"
height="250"
@onload="OnLoad"
src="@Source" />
<!-- Solution #2 : background style URL -->
<div
style="width: 250px; height: 250px; background: url(@Source)">
</div>
<!-- Solution #3 : SRC of the picture element - does not work -->
<picture style="width: 250px; height: 250px">
<source srcset="@Source" type="image/png" media="(min-width:250px)">
</picture>
@code
{
private Random _generator = new();
public string Source { get; set; } = "/source";
public void OnLoad()
{
// Animate by creating infinite loop of HTTP calls
// updating image source right after loading the previous one
var uid = Guid.NewGuid().ToString("N");
var num = _generator.Next();
var stamp = DateTime.Now.Ticks;
Source = $"/source?{ uid }-{ num }-{ stamp }";
}
}
The outcome
Red border is HTML animation. Blue border is CSS background. Picture tag didn't render.
The questions
- Why solution #3 using
picture
element isn't working? - Why solution #2 with CSS background is much slower than solution #1 with image tag. Why is it skipping some frames and is not GPU accelerated?
- Is there a way to decrease load on CPU by changing something in HTML or in the controller, e.g. switching to async streams in controller or convert images to a video stream?
Update
Appeared to be there is much bigger issue with refreshing img
URL. Looks like FileStreamResult
returned from ASP controller gets locked by an image, so every time I request an image update, like
- /source?1
- /source/2
- /source?3
...browser creates and caches this image and .NET can't release this resource which leads to enormous memory increase.