diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs index c38f620c4..62c4ff59e 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSize.cs @@ -72,7 +72,7 @@ internal enum Av1BlockSize : byte /// A block of samples, 64 samples wide and 16 samples high. Block64x16 = 21, - SizesAll = 22, + AllSizes = 22, SizeS = Block4x16, Invalid = 255, Largest = SizeS - 1, diff --git a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs index 3c5ce9e59..6029d4fdd 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Av1BlockSizeExtensions.cs @@ -1,7 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using SixLabors.ImageSharp.Formats.Heif.Av1.Quantization; using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; namespace SixLabors.ImageSharp.Formats.Heif.Av1; @@ -104,7 +103,7 @@ internal static class Av1BlockSizeExtensions { Av1BlockSize planeBlockSize = blockSize.GetSubsampled(subX, subY); Av1TransformSize uvTransformSize = Av1TransformSize.Invalid; - if (planeBlockSize < Av1BlockSize.SizesAll) + if (planeBlockSize < Av1BlockSize.AllSizes) { uvTransformSize = planeBlockSize.GetMaximumTransformSize(); } diff --git a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs index 48c14ba2a..c03b3b440 100644 --- a/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs +++ b/src/ImageSharp/Formats/Heif/Av1/Transform/Av1TransformTypeExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Six Labors. // Licensed under the Six Labors Split License. -using System; - namespace SixLabors.ImageSharp.Formats.Heif.Av1.Transform; internal static class Av1TransformTypeExtensions diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs new file mode 100644 index 000000000..79b32e92a --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1BlockSizeTests.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Heif.Av1; +using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; + +[Trait("Format", "Avif")] +public class Av1BlockSizeTests +{ + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetWidthReturnsCorrectWidth(int s) + { + // Assign + Av1BlockSize blockSize = (Av1BlockSize)s; + int expectedWidth = blockSize switch + { + Av1BlockSize.Block4x4 or Av1BlockSize.Block4x8 or Av1BlockSize.Block4x16 => 4, + Av1BlockSize.Block8x4 or Av1BlockSize.Block8x8 or Av1BlockSize.Block8x16 or Av1BlockSize.Block8x32 => 8, + Av1BlockSize.Block16x4 or Av1BlockSize.Block16x8 or Av1BlockSize.Block16x16 or Av1BlockSize.Block16x32 or Av1BlockSize.Block16x64 => 16, + Av1BlockSize.Block32x8 or Av1BlockSize.Block32x16 or Av1BlockSize.Block32x32 or Av1BlockSize.Block32x64 => 32, + Av1BlockSize.Block64x16 or Av1BlockSize.Block64x32 or Av1BlockSize.Block64x64 or Av1BlockSize.Block64x128 => 64, + Av1BlockSize.Block128x64 or Av1BlockSize.Block128x128 => 128, + _ => -1 + }; + + // Act + int actualWidth = blockSize.GetWidth(); + + // Assert + Assert.Equal(expectedWidth, actualWidth); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetHeightReturnsCorrectHeight(int s) + { + // Assign + Av1BlockSize blockSize = (Av1BlockSize)s; + int expectedHeight = blockSize switch + { + Av1BlockSize.Block4x4 or Av1BlockSize.Block8x4 or Av1BlockSize.Block16x4 => 4, + Av1BlockSize.Block4x8 or Av1BlockSize.Block8x8 or Av1BlockSize.Block16x8 or Av1BlockSize.Block32x8 => 8, + Av1BlockSize.Block4x16 or Av1BlockSize.Block8x16 or Av1BlockSize.Block16x16 or Av1BlockSize.Block32x16 or Av1BlockSize.Block64x16 => 16, + Av1BlockSize.Block8x32 or Av1BlockSize.Block16x32 or Av1BlockSize.Block32x32 or Av1BlockSize.Block64x32 => 32, + Av1BlockSize.Block16x64 or Av1BlockSize.Block32x64 or Av1BlockSize.Block64x64 or Av1BlockSize.Block128x64 => 64, + Av1BlockSize.Block64x128 or Av1BlockSize.Block128x128 => 128, + _ => -1 + }; + + // Act + int actualHeight = blockSize.GetHeight(); + + // Assert + Assert.Equal(expectedHeight, actualHeight); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetSubSampledReturnsCorrectSize(int s) + { + if (s is 0 or 1 or 2 or 16 or 17) + { + // Exceptional values, skip for this generic test. + return; + } + + // Assign + Av1BlockSize blockSize = (Av1BlockSize)s; + int originalWidth = blockSize.GetWidth(); + int originalHeight = blockSize.GetHeight(); + + // Act + Av1BlockSize actualNoNo = blockSize.GetSubsampled(false, false); + Av1BlockSize actualYesNo = blockSize.GetSubsampled(true, false); + Av1BlockSize actualNoYes = blockSize.GetSubsampled(false, true); + Av1BlockSize actualYesYes = blockSize.GetSubsampled(true, true); + + // Assert + Assert.Equal(originalWidth, actualNoNo.GetWidth()); + Assert.Equal(originalHeight, actualNoNo.GetHeight()); + + if (actualYesNo != Av1BlockSize.Invalid) + { + Assert.Equal(originalWidth, actualYesNo.GetWidth() * 2); + Assert.Equal(originalHeight, actualYesNo.GetHeight()); + } + + if (actualNoYes != Av1BlockSize.Invalid) + { + Assert.Equal(originalWidth, actualNoYes.GetWidth()); + Assert.Equal(originalHeight, actualNoYes.GetHeight() * 2); + } + + Assert.Equal(originalWidth, actualYesYes.GetWidth() * 2); + Assert.Equal(originalHeight, actualYesYes.GetHeight() * 2); + } + + public static TheoryData GetAllSizes() + { + TheoryData combinations = []; + for (int s = 0; s < (int)Av1BlockSize.AllSizes; s++) + { + combinations.Add(s); + } + + return combinations; + } + + private static int GetRatio(Av1BlockSize blockSize) + { + int width = blockSize.GetWidth(); + int height = blockSize.GetHeight(); + int ratio = width > height ? width / height : height / width; + return ratio; + } +} diff --git a/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TransformSizeTests.cs b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TransformSizeTests.cs new file mode 100644 index 000000000..d0740edc3 --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Heif/Av1/Av1TransformSizeTests.cs @@ -0,0 +1,152 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +using SixLabors.ImageSharp.Formats.Heif.Av1; +using SixLabors.ImageSharp.Formats.Heif.Av1.Transform; + +namespace SixLabors.ImageSharp.Tests.Formats.Heif.Av1; + +[Trait("Format", "Avif")] +public class Av1TransformSizeTests +{ + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetWidthReturnsCorrectWidth(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int expectedWidth = transformSize switch + { + Av1TransformSize.Size4x4 or Av1TransformSize.Size4x8 or Av1TransformSize.Size4x16 => 4, + Av1TransformSize.Size8x4 or Av1TransformSize.Size8x8 or Av1TransformSize.Size8x16 or Av1TransformSize.Size8x32 => 8, + Av1TransformSize.Size16x4 or Av1TransformSize.Size16x8 or Av1TransformSize.Size16x16 or Av1TransformSize.Size16x32 or Av1TransformSize.Size16x64 => 16, + Av1TransformSize.Size32x8 or Av1TransformSize.Size32x16 or Av1TransformSize.Size32x32 or Av1TransformSize.Size32x64 => 32, + Av1TransformSize.Size64x16 or Av1TransformSize.Size64x32 or Av1TransformSize.Size64x64 => 64, + _ => -1 + }; + + // Act + int actualWidth = transformSize.GetWidth(); + + // Assert + Assert.Equal(expectedWidth, actualWidth); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetHeightReturnsCorrectHeight(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int expectedHeight = transformSize switch + { + Av1TransformSize.Size4x4 or Av1TransformSize.Size8x4 or Av1TransformSize.Size16x4 => 4, + Av1TransformSize.Size4x8 or Av1TransformSize.Size8x8 or Av1TransformSize.Size16x8 or Av1TransformSize.Size32x8 => 8, + Av1TransformSize.Size4x16 or Av1TransformSize.Size8x16 or Av1TransformSize.Size16x16 or Av1TransformSize.Size32x16 or Av1TransformSize.Size64x16 => 16, + Av1TransformSize.Size8x32 or Av1TransformSize.Size16x32 or Av1TransformSize.Size32x32 or Av1TransformSize.Size64x32 => 32, + Av1TransformSize.Size16x64 or Av1TransformSize.Size32x64 or Av1TransformSize.Size64x64 => 64, + _ => -1 + }; + + // Act + int actualHeight = transformSize.GetHeight(); + + // Assert + Assert.Equal(expectedHeight, actualHeight); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetSubSizeReturnsCorrectRatio(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int ratio = GetRatio(transformSize); + int expectedRatio = (ratio == 4) ? 2 : 1; + + // Act + Av1TransformSize actual = transformSize.GetSubSize(); + int actualRatio = GetRatio(actual); + + // Assert + Assert.Equal(expectedRatio, actualRatio); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetSquareSizeReturnsCorrectRatio(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int ratio = GetRatio(transformSize); + int expectedRatio = 1; + int expectedSize = Math.Min(transformSize.GetWidth(), transformSize.GetHeight()); + + // Act + Av1TransformSize actual = transformSize.GetSquareSize(); + int actualRatio = GetRatio(actual); + + // Assert + Assert.Equal(expectedRatio, actualRatio); + Assert.Equal(expectedSize, actual.GetWidth()); + Assert.Equal(expectedSize, actual.GetHeight()); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void GetSquareUpSizeReturnsCorrectRatio(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int ratio = GetRatio(transformSize); + int expectedRatio = 1; + int expectedSize = Math.Max(transformSize.GetWidth(), transformSize.GetHeight()); + + // Act + Av1TransformSize actual = transformSize.GetSquareUpSize(); + int actualRatio = GetRatio(actual); + + // Assert + Assert.Equal(expectedRatio, actualRatio); + Assert.Equal(expectedSize, actual.GetWidth()); + Assert.Equal(expectedSize, actual.GetHeight()); + } + + [Theory] + [MemberData(nameof(GetAllSizes))] + internal void ToBlockSizeReturnsSameWidthAndHeight(int s) + { + // Assign + Av1TransformSize transformSize = (Av1TransformSize)s; + int transformWidth = transformSize.GetWidth(); + int transformHeight = transformSize.GetHeight(); + + // Act + Av1BlockSize blockSize = transformSize.ToBlockSize(); + int blockWidth = blockSize.GetWidth(); + int blockHeight = blockSize.GetHeight(); + + // Assert + Assert.Equal(transformWidth, blockWidth); + Assert.Equal(transformHeight, blockHeight); + } + + public static TheoryData GetAllSizes() + { + TheoryData combinations = []; + for (int s = 0; s < (int)Av1TransformSize.AllSizes; s++) + { + combinations.Add(s); + } + + return combinations; + } + + private static int GetRatio(Av1TransformSize transformSize) + { + int width = transformSize.GetWidth(); + int height = transformSize.GetHeight(); + int ratio = width > height ? width / height : height / width; + return ratio; + } +}