|
| 1 | +using System; |
| 2 | +using BCnEncoder.Shared; |
| 3 | +using BCnEncTests.Support; |
| 4 | +using CommunityToolkit.HighPerformance; |
| 5 | +using SixLabors.ImageSharp; |
| 6 | +using SixLabors.ImageSharp.Processing; |
| 7 | +using Xunit; |
| 8 | +using Xunit.Abstractions; |
| 9 | + |
| 10 | +namespace BCnEncTests |
| 11 | +{ |
| 12 | + public class MipMapperTests |
| 13 | + { |
| 14 | + private readonly ITestOutputHelper output; |
| 15 | + |
| 16 | + public MipMapperTests(ITestOutputHelper output) => this.output = output; |
| 17 | + |
| 18 | + [Fact] |
| 19 | + public void MipChainHasCorrectDimensions() |
| 20 | + { |
| 21 | + var image = ImageLoader.TestGradient1; // 512x416 |
| 22 | + var numMips = 0; |
| 23 | + var chain = MipMapper.GenerateMipChain(image, ref numMips); |
| 24 | + |
| 25 | + Assert.Equal(chain.Length, numMips); |
| 26 | + Assert.True(numMips > 1); |
| 27 | + |
| 28 | + for (var i = 0; i < numMips; i++) |
| 29 | + { |
| 30 | + Assert.Equal(Math.Max(1, image.Width >> i), chain[i].Width); |
| 31 | + Assert.Equal(Math.Max(1, image.Height >> i), chain[i].Height); |
| 32 | + } |
| 33 | + |
| 34 | + // Last level must be 1x1 |
| 35 | + Assert.Equal(1, chain[numMips - 1].Width); |
| 36 | + Assert.Equal(1, chain[numMips - 1].Height); |
| 37 | + } |
| 38 | + |
| 39 | + /// <summary> |
| 40 | + /// Compares each mip level produced by MipMapper against the equivalent |
| 41 | + /// ImageSharp Box-filter resize (Compand=false so both operate in sRGB |
| 42 | + /// byte space without gamma correction). The PSNR should be very high |
| 43 | + /// because both algorithms perform the same 2x2 box average. |
| 44 | + /// </summary> |
| 45 | + [Theory] |
| 46 | + [InlineData(1)] |
| 47 | + [InlineData(2)] |
| 48 | + [InlineData(3)] |
| 49 | + [InlineData(4)] |
| 50 | + public void MipLevelMatchesImageSharpBoxFilter(int mipLevel) |
| 51 | + { |
| 52 | + var image = ImageLoader.TestGradient1; |
| 53 | + var numMips = mipLevel + 1; |
| 54 | + var chain = MipMapper.GenerateMipChain(image, ref numMips); |
| 55 | + |
| 56 | + var mipW = chain[mipLevel].Width; |
| 57 | + var mipH = chain[mipLevel].Height; |
| 58 | + |
| 59 | + // ImageSharp Box sampler + Compand=false: no gamma correction, |
| 60 | + // averages pixel values in sRGB space — matches MipMapper exactly. |
| 61 | + using var imageSharp = ImageLoader.LoadTestImageSharp("../../../testImages/test_gradient_1_512.jpg"); |
| 62 | + using var reference = imageSharp.Clone(x => x.Resize(new ResizeOptions |
| 63 | + { |
| 64 | + Size = new Size(mipW, mipH), |
| 65 | + Sampler = KnownResamplers.Box, |
| 66 | + Compand = false |
| 67 | + })); |
| 68 | + |
| 69 | + var mipColors = TestHelper.GetSinglePixelArrayAsColors(chain[mipLevel]); |
| 70 | + var refColors = TestHelper.GetSinglePixelArrayAsColors(reference); |
| 71 | + |
| 72 | + var psnr = ImageQuality.PeakSignalToNoiseRatio(mipColors, refColors); |
| 73 | + output.WriteLine($"Mip level {mipLevel} ({mipW}x{mipH}): PSNR vs ImageSharp Box = {psnr:F2} dB"); |
| 74 | + |
| 75 | + Assert.True(psnr > 40, |
| 76 | + $"Mip level {mipLevel}: PSNR vs ImageSharp Box was {psnr:F2} dB (expected > 40)"); |
| 77 | + } |
| 78 | + } |
| 79 | +} |
0 commit comments