Phase 7: cockpit overlay generator, region extraction, and profile editor
Overlay generator (pure, tested) — RioJoy.Core/Overlay:
- FontFitter ports the legacy calc-fontsize auto-fit search (validated against a
brute-force oracle); OverlayLayoutEngine ports create-data-layer's fit + h/v
justification and adds per-region 90 deg CCW rotation; OverlayTemplate/Region +
OverlayTemplateStore hold cell geometry/colour/rotation (regions.json).
- GoobieDataImporter parses the legacy .data label sheet into label rows.
- src/RioJoy.Overlay: SkiaSharp rasterizer (SkiaTextMeasurer shared with the engine
so measured layout == drawn output; SkiaOverlayRenderer -> PNG;
ProfileWallpaperGenerator). Verified end-to-end on the real cockpit art
(regions.json + riojoy.png + TEST.data) by OverlayRenderIntegrationTests.
- RioJoy.Tray/WallpaperApplier applies via SystemParametersInfo; RioCoordinator
generates+applies on profile activation (opt-in via AppConfig.OverlayTemplatePath).
Region geometry — tools/XcfRegionExtract parses riojoy.xcf (a GIMP-format binary
reader, no GIMP needed) into the 119-cell regions.json: per-layer offsets/size/
font/colour, with 90 deg CCW rotation on a-00 + b-10..b-1F. Base image riojoy.png.
NOTE: the wallpaper positions are a VGA chroma-split display artifact (6 displays),
not the cockpit's logical layout.
Mapping editor (first cut) — RioJoy.Tray/Editor/ProfileEditorForm renders the
config sheet's logical grid (SheetLayout parses the sheet CSV export). The iRIO
word is edited via ButtonBinding <-> RioMapEntry.Create (no hex bit-twiddling), with
a context-sensitive picker: keyboard keys by name (KeyCatalog VK names), joystick
Button N, hat direction, mouse/RIO-command enum names; modifiers only for keyboard.
Opened from the tray ("Edit profile..."). The sheet-grid layout is a starting point
that needs rework from a better-formatted source.
~220 xUnit tests green. Docs (PLAN.md, README) updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
using RioJoy.Core.Overlay;
|
||||
using RioJoy.Overlay;
|
||||
using SkiaSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace RioJoy.Core.Tests.Overlay;
|
||||
|
||||
/// <summary>
|
||||
/// Full Phase-7 pipeline on the real cockpit assets: extracted regions.json + the
|
||||
/// exported riojoy.png + the legacy TEST.data labels → a rendered wallpaper. Proves
|
||||
/// the extract → import → lay out → rasterize chain end to end (headless).
|
||||
/// </summary>
|
||||
public class OverlayRenderIntegrationTests
|
||||
{
|
||||
private static bool RegionDiffers(SKBitmap a, SKBitmap b, OverlayRegion r)
|
||||
{
|
||||
int x0 = (int)r.X, y0 = (int)r.Y;
|
||||
int x1 = Math.Min(a.Width, (int)(r.X + r.Width));
|
||||
int y1 = Math.Min(a.Height, (int)(r.Y + r.Height));
|
||||
for (int y = y0; y < y1; y++)
|
||||
for (int x = x0; x < x1; x++)
|
||||
if (a.GetPixel(x, y) != b.GetPixel(x, y))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RendersCockpitWallpaper_FromRealAssets()
|
||||
{
|
||||
OverlayTemplate template = OverlayTemplateStore.Load(TestRepo.CustomBackground("regions.json"));
|
||||
Assert.False(string.IsNullOrWhiteSpace(template.BaseImagePath));
|
||||
|
||||
using SKBitmap baseImg = SKBitmap.Decode(TestRepo.CustomBackground(template.BaseImagePath!));
|
||||
Assert.NotNull(baseImg);
|
||||
Assert.Equal(template.Width, baseImg.Width);
|
||||
Assert.Equal(template.Height, baseImg.Height);
|
||||
|
||||
IReadOnlyDictionary<string, string> labels =
|
||||
GoobieDataImporter.LabelsForRow(GoobieDataImporter.Load(TestRepo.CustomBackground("TEST.data")));
|
||||
|
||||
using SKBitmap rendered = new SkiaOverlayRenderer().Render(template, labels, baseImg);
|
||||
|
||||
Assert.Equal(2720, rendered.Width);
|
||||
Assert.Equal(600, rendered.Height);
|
||||
|
||||
// A labeled cell ("b-00" -> "LSDI") must have changed from the base.
|
||||
OverlayRegion b00 = template.FindRegion("b-00")!;
|
||||
Assert.True(RegionDiffers(baseImg, rendered, b00), "labelled region b-00 should differ from the base image");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user