Four reports, three causes: 1. "Background plate has no code behind it" — the plate COLOUR and OPACITY controls were disabled until the plate switch was on, which reads as dead controls. They are always settable now; the switch is what decides whether a plate is drawn. 2. "Opacity doesn't work" — proven in the renderer test: 20% and 80% produce alphas of ~51 and ~204, so the slider reaches the pixels. What was actually wrong was the box. 3. The strip's BOX was fixed at 56px while a wrapped strip renders taller — the frame was scaled into the box and looked squashed/clipped. The source's width/height now follow the rendered frame, and its Y follows from the edge. 4. "Simulate (dev) should override Show the scoreboard" — it does: the preview gate is (_scoreboardEnabled || _scoreboardSimulate), so the dev toggle works even with the product setting off. Not changed: the strip is STATIC by design (all enabled facts always visible, wrap rather than scroll). If scrolling is wanted after all, that is a presentation mode to add, not a fix.
89 lines
4.3 KiB
C#
89 lines
4.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using Xunit;
|
|
using ytLive.Services;
|
|
using ytLive.Services.Compositor;
|
|
|
|
namespace ytLive.Tests;
|
|
|
|
/// <summary>The strip is state, not events: everything enabled is always on screen. These checks
|
|
/// paint the real frame, because a renderer that silently draws nothing would still pass a
|
|
/// structural test.</summary>
|
|
public class ScoreboardRendererTests
|
|
{
|
|
private static readonly List<string> Tiles = new() { "Members 12", "Milestones 3", "SC $47.00" };
|
|
|
|
[Fact]
|
|
public void The_strip_paints_the_plate_and_the_text_and_never_needs_a_scroll()
|
|
{
|
|
Exception? failure = null;
|
|
|
|
var thread = new Thread(() =>
|
|
{
|
|
try
|
|
{
|
|
// Plate on: half-transparent blue behind white text.
|
|
var plated = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.5));
|
|
Assert.NotNull(plated);
|
|
Assert.Equal(ScoreboardRenderer.Width, plated!.Width);
|
|
Assert.True(plated.Height > 0);
|
|
|
|
Assert.True(Count(plated, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a is > 100 and < 170) > 500,
|
|
"the plate must fill at the chosen colour and opacity");
|
|
Assert.True(Count(plated, (r, g, b, a) => r > 200 && g > 200 && b > 200 && a > 200) > 20,
|
|
"the text must be painted in the chosen colour");
|
|
|
|
// Plate off: the same area is transparent, the text survives.
|
|
var bare = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", false, "#0000FF", 0.5));
|
|
Assert.NotNull(bare);
|
|
Assert.Equal(0, Count(bare!, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a > 100));
|
|
Assert.True(Count(bare!, (r, g, b, a) => r > 200 && g > 200 && b > 200 && a > 200) > 20);
|
|
|
|
// Text colour reaches the glyphs.
|
|
var red = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FF0000", false, "#000000", 0.5));
|
|
Assert.True(Count(red!, (r, g, b, a) => r > 180 && g < 80 && b < 80 && a > 200) > 20);
|
|
|
|
// The PLATE TOGGLE itself: same text, plate off vs on, different pixels. This is the
|
|
// check that answers "the checkbox has no code behind it".
|
|
var off = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", false, "#0000FF", 0.5));
|
|
var on = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.5));
|
|
Assert.True(Count(on!, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a > 100)
|
|
> Count(off!, (r, g, b, a) => b > 150 && r < 60 && g < 60 && a > 100) + 500,
|
|
"turning the plate on must add plate pixels");
|
|
|
|
// Opacity: 20% and 80% must produce visibly different plate transparency.
|
|
var faint = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.2));
|
|
var solid = ScoreboardRenderer.Render(Tiles, new ScoreboardStyle("#FFFFFF", true, "#0000FF", 0.8));
|
|
int AlphaOf(VideoFrame f)
|
|
{
|
|
for (var i = 3; i < f.BgraPixels.Length; i += 4)
|
|
if (f.BgraPixels[i] is > 10 and < 245) return f.BgraPixels[i];
|
|
return 0;
|
|
}
|
|
Assert.InRange(AlphaOf(faint!), 35, 70); // ~20% of 255
|
|
Assert.InRange(AlphaOf(solid!), 190, 220); // ~80% of 255
|
|
Assert.True(AlphaOf(solid!) > AlphaOf(faint!) + 100, "the opacity slider must move the alpha");
|
|
|
|
// Nothing enabled: no frame at all (the layer draws nothing).
|
|
Assert.Null(ScoreboardRenderer.Render(Array.Empty<string>(), new ScoreboardStyle()));
|
|
}
|
|
catch (Exception ex) { failure = ex; }
|
|
});
|
|
thread.SetApartmentState(ApartmentState.STA);
|
|
thread.Start();
|
|
thread.Join();
|
|
|
|
Assert.Null(failure);
|
|
}
|
|
|
|
private static int Count(VideoFrame frame, Func<int, int, int, int, bool> predicate)
|
|
{
|
|
var hits = 0;
|
|
for (var i = 0; i < frame.BgraPixels.Length; i += 4)
|
|
if (predicate(frame.BgraPixels[i + 2], frame.BgraPixels[i + 1], frame.BgraPixels[i], frame.BgraPixels[i + 3]))
|
|
hits++;
|
|
return hits;
|
|
}
|
|
}
|