From fb207199f5b3db589f8a2013caeb72f238e6c8e7 Mon Sep 17 00:00:00 2001 From: Erik White <26148654+Erik-White@users.noreply.github.com> Date: Thu, 11 Jun 2026 16:38:29 +0200 Subject: [PATCH] Clean up/clarify test helper method --- .../Formats/Png/PngCgbiProcessorTests.cs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs index 426afb6d42..cd03c5e488 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngCgbiProcessorTests.cs @@ -120,34 +120,42 @@ public class PngCgbiProcessorTests Assert.True(vectorTail.SequenceEqual(inputTail)); } + /// + /// Builds synthetic input for tests. Produces inputs that exercise all three alpha cases. + /// + /// Channel values laid out as a defiltered CgBI scanline in premultiplied BGRA order private static byte[] CreateBgraScanline(int pixelCount) { - // Deterministic mix of edge cases (a=0, a=255, partial alpha) and varied channels. + // The distinct strides keep the channels from being equal to each other or constant across pixels + // So a bug that e.g. swaps two channels or reuses one channel's value doesn't accidentally pass + const int alphaCaseCount = 7; + const int redStride = 13; + const int greenStride = 29; + const int blueStride = 53; + byte[] bytes = new byte[pixelCount * 4]; for (int p = 0; p < pixelCount; p++) { - byte a = (p % 7) switch + // Cycling alpha through [0..255], and an odd partial value every pixel rotation + // ensures all three branches get covered within any scanline of 3 or more pixels + byte a = (p % alphaCaseCount) switch { 0 => byte.MinValue, 1 => byte.MaxValue, - _ => (byte)((((p * 37) + 23) & 0xFF) | 1) // never zero + _ => (byte)(((p * 37) + 23) | 1) // Produce a spread of alpha values and make sure to never get 0 }; - // CgBI premultiplied BGRA: c' = c * a / 255 - byte r = (byte)((p * 13) & 0xFF); - byte g = (byte)((p * 29) & 0xFF); - byte b = (byte)((p * 53) & 0xFF); - r = (byte)((r * a) / byte.MaxValue); - g = (byte)((g * a) / byte.MaxValue); - b = (byte)((b * a) / byte.MaxValue); - - bytes[(p * 4) + 0] = b; - bytes[(p * 4) + 1] = g; - bytes[(p * 4) + 2] = r; - bytes[(p * 4) + 3] = a; + int offset = p * 4; + bytes[offset + 0] = Premultiply((byte)(p * blueStride), a); + bytes[offset + 1] = Premultiply((byte)(p * greenStride), a); + bytes[offset + 2] = Premultiply((byte)(p * redStride), a); + bytes[offset + 3] = a; } return bytes; + + // CgBI stores channels premultiplied by alpha + static byte Premultiply(byte channel, byte alpha) => (byte)(channel * alpha / byte.MaxValue); } private static void ApplyCgbiTransformScalarReference(Span scanline)