Skip to content

Instantly share code, notes, and snippets.

@JoeStead
Created June 4, 2025 15:20
Show Gist options
  • Select an option

  • Save JoeStead/f7e2ebdb12c9b5cd54b937e1fc19396e to your computer and use it in GitHub Desktop.

Select an option

Save JoeStead/f7e2ebdb12c9b5cd54b937e1fc19396e to your computer and use it in GitHub Desktop.
namespace Dig.Api.ImageGeneration;
using SkiaSharp;
using Storage;
public class SkiaImageRenderer : IImageRenderer
{
private readonly HttpClient httpClient;
public SkiaImageRenderer(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public async Task<byte[]> RenderImage(Template template, Dictionary<string, string> parameters)
{
parameters.TryGetValue("name", out var name);
using var bitmap = new SKBitmap(template.Width, template.Height);
var canvas = new SKCanvas(bitmap);
canvas.Clear(SKColor.Parse(template.BackgroundColour));
using var paint = new SKPaint();
paint.IsAntialias = true;
await this.DrawContainers(canvas, paint, template.Containers, parameters);
using var image = SKImage.FromBitmap(bitmap);
using var data = image.Encode(SKEncodedImageFormat.Jpeg, 100);
return data.ToArray();
}
private async Task DrawContainers(SKCanvas canvas, SKPaint paint, Container[] templateContainers, Dictionary<string, string> parameters)
{
var orderedContainers = templateContainers.OrderBy(c => c.Order).ToList();
foreach (var orderedContainer in orderedContainers)
{
await orderedContainer.Render(canvas, paint, parameters, this.httpClient);
}
}
}
public static class SkiaImageExtensions
{
public static async Task Render(this Container container, SKCanvas canvas, SKPaint paint, Dictionary<string, string> parameters, HttpClient httpClient, Container? parent = null)
{
switch (container)
{
case TextContainer textContainer:
await textContainer.Render(canvas, paint, parameters, httpClient, parent);
break;
case ImageContainer imageContainer:
await imageContainer.Render(canvas, paint, parameters, httpClient, parent);
break;
default:
throw new ArgumentException("Could not determine type of container to render", nameof(container));
}
}
private static async Task Render(this TextContainer container, SKCanvas canvas, SKPaint paint, Dictionary<string, string> parameters, HttpClient httpClient, Container? parent = null)
{
var parentX = parent?.X ?? 0;
var parentY = parent?.Y ?? 0;
using var typeface = await LoadTypefaceFromUrl(container.FontUrl, httpClient);
using var font = new SKFont();
font.Typeface = typeface;
font.Size = container.FontSize;
paint.Color = SKColor.Parse(container.Colour);
canvas.DrawText(container.DefaultText, parentX + container.X, parentY + container.Y, font, paint);
}
private static async Task Render(this ImageContainer container, SKCanvas canvas, SKPaint paint, Dictionary<string, string> parameters, HttpClient httpClient, Container? parent = null)
{
var parentX = parent?.X ?? 0;
var parentY = parent?.Y ?? 0;
using var image = await LoadImageFromUrl(container.ImageUrl, httpClient);
//TODO: Use image stretch options
canvas.DrawImage(image, new SKRect(parentX + container.X,
parentY + container.Y,
parentX + container.X + container.Width,
parentY + container.Y + container.Height),
paint);
await Task.WhenAll(container.Containers.Select(c => c.Render(canvas, paint, parameters, httpClient, container)));
}
private static async Task<SKTypeface> LoadTypefaceFromUrl(string fontUrl, HttpClient httpClient)
{
var fontData = await httpClient.GetByteArrayAsync(fontUrl);
using var skData = SKData.CreateCopy(fontData);
return SKTypeface.FromData(skData);
}
public static async Task<SKImage> LoadImageFromUrl(string imageUrl, HttpClient httpClient)
{
var imageData = await httpClient.GetByteArrayAsync(imageUrl);
return SKImage.FromEncodedData(imageData);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment