Files
riojoy/src/RioJoy.Overlay/ProfileWallpaperGenerator.cs
T
CydandClaude Fable 5 1ff0b16015 Phase 8A (2/2): RioJoy.Core multi-targets net48 + net40 (Windows XP flavor)
- TargetFrameworks net48;net40. net48 keeps x64 + ViGEm + System.IO.Ports
  package; net40 adds Microsoft.Bcl.Async + System.ValueTuple and uses the
  in-box SerialPort.
- Compat/TaskCompat bridges Task.Run/Delay/WhenAny/WhenAll (TaskEx on
  net40) and SemaphoreSlim.WaitAsync (net40 blocks briefly - trivial at
  9600 baud).
- IReadOnlyList/IReadOnlyDictionary -> IList/IDictionary throughout
  (net40 predates the IReadOnly* interfaces and the Bcl backport cannot
  make arrays implement them).
- HashCode.Combine replaced with a manual combine (Bcl.HashCode has no
  net40 build); Marshal.SizeOf<T> -> typeof form; ViGEmJoystickSink
  gated #if !NET40. HidFeederJoystickSink stays on both flavors - it
  will drive RioGamepadXP.sys on XP via the same contract.

Both TFMs build; 275 tests green on net48.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:41:58 -05:00

57 lines
2.4 KiB
C#

using RioJoy.Core.Overlay;
using SkiaSharp;
namespace RioJoy.Overlay;
/// <summary>
/// Generates a profile's cockpit wallpaper: renders a label set onto the overlay
/// template's base image and writes a PNG. Resolves the template's
/// <see cref="OverlayTemplate.BaseImagePath"/> relative to the template file's
/// directory (so a <c>regions.json</c> + sibling <c>riojoy.png</c> work as a unit).
/// The runtime calls this on profile activation; an editor can call it for preview.
/// </summary>
public sealed class ProfileWallpaperGenerator
{
private readonly SkiaOverlayRenderer _renderer;
public ProfileWallpaperGenerator(SkiaOverlayRenderer? renderer = null) =>
_renderer = renderer ?? new SkiaOverlayRenderer();
/// <summary>
/// Render <paramref name="labels"/> onto the template's base image and write a
/// PNG to <paramref name="outputPath"/>; returns the output path.
/// </summary>
/// <param name="templateDirectory">
/// Directory the template's relative <see cref="OverlayTemplate.BaseImagePath"/>
/// is resolved against (normally the folder holding the regions.json).
/// </param>
public string Generate(
OverlayTemplate template,
string templateDirectory,
IDictionary<string, string> labels,
string outputPath,
OverlayLayoutOptions? options = null)
{
if (template is null) throw new ArgumentNullException(nameof(template));
if (labels is null) throw new ArgumentNullException(nameof(labels));
if (string.IsNullOrWhiteSpace(outputPath)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(outputPath));
if (string.IsNullOrWhiteSpace(template.BaseImagePath))
throw new InvalidOperationException("OverlayTemplate.BaseImagePath is not set.");
string basePath = Path.IsPathRooted(template.BaseImagePath)
? template.BaseImagePath
: Path.Combine(templateDirectory, template.BaseImagePath);
using SKBitmap baseImage = SKBitmap.Decode(basePath)
?? throw new InvalidOperationException($"Could not decode base image: {basePath}");
byte[] png = _renderer.RenderToPng(template, labels, baseImage, options);
string? dir = Path.GetDirectoryName(Path.GetFullPath(outputPath));
if (!string.IsNullOrEmpty(dir))
Directory.CreateDirectory(dir);
File.WriteAllBytes(outputPath, png);
return outputPath;
}
}