I'm trying to draw a set of images with SkiaSharp, convert them to byte array, and send to HttpContext
stream. For some reason, every image is being displayed in the browser only when the next image has been rendered and sent to the HTTP stream.
- After rendering the 1st image, browser shows empty white area, no image
- After rendering the 2nd image, browser shows 1st image
- After rendering the 3rd image, browser shows 2nd image, and so on
Here is the Web API controller. Uncommenting second drawShapes
call will make it work, but I would like to understand why image rendering is delayed. Maybe I just don't call some important method, like Flush
or something similar?
Also, this is not an ASP issue, because I tried rendering images with System.Drawing.Common and images were rendered in real time without delay.
Does anybody know what is missing in this method?
[Route("/source")]
public async Task Get()
{
// Initialize Skia
var res = new byte[1000];
var boundary = Guid.NewGuid().ToString();
var generator = new Random();
var map = new SKBitmap(250, 250);
var canvas = new SKCanvas(map);
var inPaint = new SKPaint
{
Color = SKColors.Black,
Style = SKPaintStyle.Fill,
FilterQuality = SKFilterQuality.High
};
var exPaint = new SKPaint
{
Color = SKColors.White,
Style = SKPaintStyle.Fill,
FilterQuality = SKFilterQuality.Low
};
// Create HTTP stream
Response.ContentType = "multipart/x-mixed-replace;boundary=" boundary;
var outputStream = Response.Body;
var cancellationToken = Request.HttpContext.RequestAborted;
// Create method posting images to HTTP stream
async Task drawShapes()
{
using (var image = SKImage.FromBitmap(map))
{
var pos = generator.Next(50, 150);
canvas.DrawRect(0, 0, 250, 250, exPaint);
canvas.DrawCircle(pos, pos, 20, inPaint);
res = image.Encode(SKEncodedImageFormat.Webp, 100).ToArray();
}
var header = $"--{ boundary }\r\nContent-Type: image/webp\r\nContent-Length: { res.Length }\r\n\r\n";
var headerData = Encoding.ASCII.GetBytes(header);
await outputStream.WriteAsync(headerData);
await outputStream.WriteAsync(res);
await outputStream.WriteAsync(Encoding.ASCII.GetBytes("\r\n"));
}
// Start HTTP stream
await Task.Run(async () =>
{
//while (true)
{
await drawShapes();
//await drawShapes(); // After uncommenting this shows the image (the previous one)
}
});
}
CodePudding user response:
This was answered in SkiaSharp discussions. https://github.com/mono/SkiaSharp/discussions/1975
Possible solutions are to make sure to Encode
image only after all drawings are done or to call Encode
on bitmap instead of image.
The simplest one is to replace this line.
//res = image.Encode(SKEncodedImageFormat.Webp, 100).ToArray(); // wrong
res = map.Encode(SKEncodedImageFormat.Webp, 100).ToArray(); // correct