From 71535ec456a6672f4d0029b12c3652f9b19e6626 Mon Sep 17 00:00:00 2001 From: Myx Date: Tue, 15 Jul 2025 22:13:17 +0200 Subject: [PATCH] Optimise for minimal memory --- HomeApi/Controllers/HomeController.cs | 17 +++++++- HomeApi/Handlers/ImageGeneration.cs | 58 +++++++++++++++++++++++++-- HomeApi/HomeApi.csproj | 1 + HomeApi/HomeApi.http | 6 --- HomeApi/appsettings.Development.json | 10 ++++- HomeApi/appsettings.json | 7 ++++ 6 files changed, 86 insertions(+), 13 deletions(-) diff --git a/HomeApi/Controllers/HomeController.cs b/HomeApi/Controllers/HomeController.cs index b9ba04a..e1f8c4e 100644 --- a/HomeApi/Controllers/HomeController.cs +++ b/HomeApi/Controllers/HomeController.cs @@ -15,12 +15,25 @@ public class HomeController(IMediator mediator) : ControllerBase return Ok(await mediator.Send(new Weather.Command())); } - [HttpGet("default.png")] + [HttpGet("default.bmp")] public async Task GetImage() { - return File(await mediator.Send(new ImageGeneration.Command()), "image/png"); + return File(await mediator.Send(new ImageGeneration.Command()), "image/bmp"); } + /*[HttpGet("screen/buffers")] + public async Task GetCombinedBuffers() + { + var (black, red) = await mediator.Send(new ImageGeneration.Command()); + + // Combine buffers + byte[] combined = new byte[black.Length + red.Length]; + Buffer.BlockCopy(black, 0, combined, 0, black.Length); + Buffer.BlockCopy(red, 0, combined, black.Length, red.Length); + + return File(combined, "application/octet-stream"); + }*/ + [HttpGet("departureboard")] public async Task>> GetDepartureBoard() { diff --git a/HomeApi/Handlers/ImageGeneration.cs b/HomeApi/Handlers/ImageGeneration.cs index ecf8c65..f09d205 100644 --- a/HomeApi/Handlers/ImageGeneration.cs +++ b/HomeApi/Handlers/ImageGeneration.cs @@ -1,11 +1,13 @@ using System.Reflection; -using HomeApi.Models; using HomeApi.Models.Configuration; using MediatR; -using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.Extensions.Options; using PuppeteerSharp; using RazorLight; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using Image = SixLabors.ImageSharp.Image; namespace HomeApi.Handlers; @@ -32,7 +34,7 @@ public static class ImageGeneration var weather = await _mediator.Send(new Weather.Command(), cancellationToken); var departureBoard = await _mediator.Send(new DepartureBoard.Command(), cancellationToken); - var model = new Image + var model = new Models.Image { Weather = weather, TimeTable = departureBoard @@ -69,7 +71,55 @@ public static class ImageGeneration }); await page.SetContentAsync(htmlContent, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } }); var stream = await page.ScreenshotStreamAsync(new ScreenshotOptions { Type = ScreenshotType.Png }); - return stream; + + return await stream.ToBmpStream(); } } + + private static async Task ToBmpStream(this Stream stream) + { + var image = await Image.LoadAsync(stream); + // Resize or crop to 800x480 if necessary + image.Mutate(x => x.Resize(800, 480)); + + // Reduce to 3-color e-paper palette + image.ProcessPixelRows(accessor => + { + for (int y = 0; y < accessor.Height; y++) + { + var row = accessor.GetRowSpan(y); + for (int x = 0; x < row.Length; x++) + { + var pixel = row[x]; + + // Compute perceived brightness (gray) + float brightness = 0.299f * pixel.R + 0.587f * pixel.G + 0.114f * pixel.B; + + if (pixel.R > 150 && pixel.G < 80 && pixel.B < 80) // RED threshold + { + row[x] = new Rgba32(255, 0, 0); // Red + } + else if (brightness > 180) + { + row[x] = new Rgba32(255, 255, 255); // White + } + else + { + row[x] = new Rgba32(0, 0, 0); // Black + } + } + } + }); + + var bmpStream = new MemoryStream(); + var bmpEncoder = new BmpEncoder + { + BitsPerPixel = BmpBitsPerPixel.Pixel24 + }; + + await image.SaveAsync(bmpStream, bmpEncoder); + bmpStream.Position = 0; + + return bmpStream; + } } \ No newline at end of file diff --git a/HomeApi/HomeApi.csproj b/HomeApi/HomeApi.csproj index a117e92..6e8f8ee 100644 --- a/HomeApi/HomeApi.csproj +++ b/HomeApi/HomeApi.csproj @@ -16,6 +16,7 @@ + diff --git a/HomeApi/HomeApi.http b/HomeApi/HomeApi.http index e57bcb3..e69de29 100644 --- a/HomeApi/HomeApi.http +++ b/HomeApi/HomeApi.http @@ -1,6 +0,0 @@ -@HomeApi_HostAddress = http://localhost:5128 - -GET {{HomeApi_HostAddress}}/weatherforecast/ -Accept: application/json - -### diff --git a/HomeApi/appsettings.Development.json b/HomeApi/appsettings.Development.json index 0d65bc5..0b65097 100644 --- a/HomeApi/appsettings.Development.json +++ b/HomeApi/appsettings.Development.json @@ -1,4 +1,11 @@ { + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:5000" + } + } + }, "Logging": { "LogLevel": { "Default": "Information", @@ -18,5 +25,6 @@ }, "DefaultCity": "Vega stockholms lan", "DefaultStation": "Vega Station" - } + }, + "AllowedHosts": "*" } diff --git a/HomeApi/appsettings.json b/HomeApi/appsettings.json index 0f339c0..0b65097 100644 --- a/HomeApi/appsettings.json +++ b/HomeApi/appsettings.json @@ -1,4 +1,11 @@ { + "Kestrel": { + "Endpoints": { + "Http": { + "Url": "http://*:5000" + } + } + }, "Logging": { "LogLevel": { "Default": "Information",