From f4678fdc01ddd459a60980501924c1a39c76f9c7 Mon Sep 17 00:00:00 2001 From: Nominom Date: Tue, 12 Jan 2021 21:04:54 +0200 Subject: [PATCH] Some more code style changes, more raw api's and duplicate encoder code removed --- .editorconfig | 17 +++ BCnEnc.Net/Decoder/BcBlockDecoder.cs | 5 +- BCnEnc.Net/Decoder/BcDecoder.cs | 50 ++++++- BCnEnc.Net/Encoder/BaseBcBlockEncoder.cs | 43 ++++++ BCnEnc.Net/Encoder/Bc1BlockEncoder.cs | 102 ++++---------- BCnEnc.Net/Encoder/Bc2BlockEncoder.cs | 50 ++----- BCnEnc.Net/Encoder/Bc3BlockEncoder.cs | 51 ++----- BCnEnc.Net/Encoder/Bc4BlockEncoder.cs | 35 ++--- BCnEnc.Net/Encoder/Bc5BlockEncoder.cs | 32 +---- BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs | 72 ++++------ BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs | 4 +- BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs | 4 +- BCnEnc.Net/Encoder/BcEncoder.cs | 117 ++++++++++++++-- BCnEnc.Net/Encoder/ColorVariationGenerator.cs | 28 ++-- BCnEnc.Net/Encoder/IBcBlockEncoder.cs | 4 +- .../Encoder/Options/EncoderOutputOptions.cs | 2 +- .../Shared/BinaryReaderWriterExtensions.cs | 1 - BCnEnc.Net/Shared/Colors.cs | 42 +++--- BCnEnc.Net/Shared/ImageToBlocks.cs | 8 +- BCnEnc.Net/Shared/MipMapper.cs | 21 ++- BCnEnc.Net/Shared/PcaVectors.cs | 20 ++- BCnEncNet.sln | 5 + BCnEncNet.sln.DotSettings | 4 + BCnEncTests/BC1Tests.cs | 2 +- BCnEncTests/Bc7BlockTests.cs | 5 +- BCnEncTests/BlockTests.cs | 3 +- BCnEncTests/ClusterTests.cs | 9 +- BCnEncTests/DdsReadTests.cs | 9 +- BCnEncTests/DdsWritingTests.cs | 19 ++- BCnEncTests/DecodingTests.cs | 2 - BCnEncTests/EncodingTest.cs | 73 +++++----- BCnEncTests/ImageLoader.cs | 26 ++-- BCnEncTests/PcaTests.cs | 4 +- BCnEncTests/RawTests.cs | 129 ++++++++++++++---- BCnEncTests/TestHelper.cs | 5 +- 35 files changed, 552 insertions(+), 451 deletions(-) create mode 100644 .editorconfig create mode 100644 BCnEnc.Net/Encoder/BaseBcBlockEncoder.cs create mode 100644 BCnEncNet.sln.DotSettings diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..1bf2168 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = tab +indent_size = 4 + +[*.{diff,md}] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/BCnEnc.Net/Decoder/BcBlockDecoder.cs b/BCnEnc.Net/Decoder/BcBlockDecoder.cs index 37dea11..c4e6bc6 100644 --- a/BCnEnc.Net/Decoder/BcBlockDecoder.cs +++ b/BCnEnc.Net/Decoder/BcBlockDecoder.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using BCnEncoder.Shared; @@ -12,14 +13,14 @@ internal interface IBcBlockDecoder } internal abstract class BaseBcBlockDecoder : IBcBlockDecoder - where T : struct + where T : unmanaged { public RawBlock4X4Rgba32[,] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight) { blockWidth = (int)MathF.Ceiling(pixelWidth / 4.0f); blockHeight = (int)MathF.Ceiling(pixelHeight / 4.0f); - if (data.Length != blockWidth * blockHeight * Marshal.SizeOf()) + if (data.Length != blockWidth * blockHeight * Unsafe.SizeOf()) { throw new InvalidDataException("Given data does not match expected length."); } diff --git a/BCnEnc.Net/Decoder/BcDecoder.cs b/BCnEnc.Net/Decoder/BcDecoder.cs index 7698856..d453ba4 100644 --- a/BCnEnc.Net/Decoder/BcDecoder.cs +++ b/BCnEnc.Net/Decoder/BcDecoder.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.CompilerServices; using System.Text; using BCnEncoder.Decoder.Options; using BCnEncoder.Shared; @@ -33,7 +34,7 @@ public class BcDecoder /// The decoded Rgba32 image. public Image DecodeRaw(Stream inputStream, CompressionFormat format, int pixelWidth, int pixelHeight) { - var dataArray = new byte[inputStream.Position]; + var dataArray = new byte[GetBufferSize(format, pixelWidth, pixelHeight)]; inputStream.Read(dataArray, 0, dataArray.Length); return DecodeRaw(dataArray, format, pixelWidth, pixelHeight); @@ -47,8 +48,13 @@ public Image DecodeRaw(Stream inputStream, CompressionFormat format, int /// The pixelWidth of the image. /// The pixelHeight of the image. /// The decoded Rgba32 image. - public Image DecodeRaw(byte[] input, CompressionFormat format, int pixelWidth, int pixelHeight) + public Image DecodeRaw(ReadOnlySpan input, CompressionFormat format, int pixelWidth, int pixelHeight) { + if (input.Length != GetBufferSize(format, pixelWidth, pixelHeight)) + { + throw new ArgumentException("The size of the input buffer does not match the expected size"); + } + var isCompressedFormat = format.IsCompressedFormat(); if (isCompressedFormat) { @@ -502,5 +508,45 @@ private IRawDecoder GetRawDecoder(CompressionFormat format) throw new ArgumentOutOfRangeException(nameof(format), format, null); } } + + private int GetBufferSize(CompressionFormat format, int pixelWidth, int pixelHeight) + { + switch (format) + { + case CompressionFormat.R: + return pixelWidth * pixelHeight; + + case CompressionFormat.Rg: + return 2 * pixelWidth * pixelHeight; + + case CompressionFormat.Rgb: + return 3 * pixelWidth * pixelHeight; + + case CompressionFormat.Rgba: + return 4 * pixelWidth * pixelHeight; + + case CompressionFormat.Bc1: + case CompressionFormat.Bc1WithAlpha: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + case CompressionFormat.Bc2: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + case CompressionFormat.Bc3: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + case CompressionFormat.Bc4: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + case CompressionFormat.Bc5: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + case CompressionFormat.Bc7: + return Unsafe.SizeOf() * ImageToBlocks.CalculateNumOfBlocks(pixelWidth, pixelHeight); + + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + } } } diff --git a/BCnEnc.Net/Encoder/BaseBcBlockEncoder.cs b/BCnEnc.Net/Encoder/BaseBcBlockEncoder.cs new file mode 100644 index 0000000..f8adebc --- /dev/null +++ b/BCnEnc.Net/Encoder/BaseBcBlockEncoder.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using BCnEncoder.Shared; + +namespace BCnEncoder.Encoder +{ + internal abstract class BaseBcBlockEncoder : IBcBlockEncoder where T : unmanaged + { + public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) + { + var outputData = new byte[blockWidth * blockHeight * Unsafe.SizeOf()]; + var outputBlocks = MemoryMarshal.Cast(outputData); + + if (parallel) + { + Parallel.For(0, blocks.Length, i => + { + var outputBlocks = MemoryMarshal.Cast(outputData); + outputBlocks[i] = EncodeBlock(blocks[i], quality); + }); + } + else + { + for (var i = 0; i < blocks.Length; i++) + { + outputBlocks[i] = EncodeBlock(blocks[i], quality); + } + } + + return outputData; + } + + public abstract GlInternalFormat GetInternalFormat(); + public abstract GlFormat GetBaseInternalFormat(); + public abstract DxgiFormat GetDxgiFormat(); + + protected abstract T EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality); + } +} diff --git a/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs index d53f86d..dc8015f 100644 --- a/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs +++ b/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs @@ -5,34 +5,9 @@ namespace BCnEncoder.Encoder { - internal class Bc1BlockEncoder : IBcBlockEncoder + internal class Bc1BlockEncoder : BaseBcBlockEncoder { - - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) - { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) - { - Parallel.For(0, blocks.Length, i => - { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else - { - for (var i = 0; i < blocks.Length; i++) - { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) + protected override Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { switch (quality) { @@ -48,17 +23,17 @@ private Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality } } - public GlInternalFormat GetInternalFormat() + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRgbS3TcDxt1Ext; } - public GlFormat GetBaseInternalFormat() + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRgb; } - public DxgiFormat GetDxgiFormat() + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc1Unorm; } @@ -127,8 +102,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) } private static class Bc1BlockEncoderBalanced { - private const int MaxTries_ = 24 * 2; - private const float ErrorThreshold_ = 0.05f; + private const int MaxTries = 24 * 2; + private const float ErrorThreshold = 0.05f; internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) { @@ -149,7 +124,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var best = TryColors(rawBlock, c0, c1, out var bestError); - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (newC0.data < newC1.data) @@ -169,7 +144,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) c1 = newC1; } - if (bestError < ErrorThreshold_) { + if (bestError < ErrorThreshold) { break; } } @@ -180,8 +155,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc1BlockEncoderSlow { - private const int MaxTries_ = 9999; - private const float ErrorThreshold_ = 0.01f; + private const int MaxTries = 9999; + private const float ErrorThreshold = 0.01f; internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) { @@ -204,7 +179,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var lastChanged = 0; - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (newC0.data < newC1.data) @@ -227,7 +202,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) lastChanged = 0; } - if (bestError < ErrorThreshold_ || lastChanged > ColorVariationGenerator.VarPatternCount) { + if (bestError < ErrorThreshold || lastChanged > ColorVariationGenerator.VarPatternCount) { break; } } @@ -239,34 +214,9 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) #endregion } - internal class Bc1AlphaBlockEncoder : IBcBlockEncoder + internal class Bc1AlphaBlockEncoder : BaseBcBlockEncoder { - - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) - { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) - { - Parallel.For(0, blocks.Length, i => - { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else - { - for (var i = 0; i < blocks.Length; i++) - { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) + protected override Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { switch (quality) { @@ -282,17 +232,17 @@ private Bc1Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality } } - public GlInternalFormat GetInternalFormat() + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRgbaS3TcDxt1Ext; } - public GlFormat GetBaseInternalFormat() + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRgba; } - public DxgiFormat GetDxgiFormat() + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc1Unorm; } @@ -370,8 +320,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc1AlphaBlockEncoderBalanced { - private const int MaxTries_ = 24 * 2; - private const float ErrorThreshold_ = 0.05f; + private const int MaxTries = 24 * 2; + private const float ErrorThreshold = 0.05f; internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) @@ -399,7 +349,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var best = TryColors(rawBlock, c0, c1, out var bestError); - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (!hasAlpha && newC0.data < newC1.data) @@ -423,7 +373,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) c1 = newC1; } - if (bestError < ErrorThreshold_) { + if (bestError < ErrorThreshold) { break; } } @@ -434,8 +384,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc1AlphaBlockEncoderSlow { - private const int MaxTries_ = 9999; - private const float ErrorThreshold_ = 0.05f; + private const int MaxTries = 9999; + private const float ErrorThreshold = 0.05f; internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) { @@ -463,7 +413,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var best = TryColors(rawBlock, c0, c1, out var bestError); var lastChanged = 0; - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (!hasAlpha && newC0.data < newC1.data) @@ -490,7 +440,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) lastChanged = 0; } - if (bestError < ErrorThreshold_ || lastChanged > ColorVariationGenerator.VarPatternCount) { + if (bestError < ErrorThreshold || lastChanged > ColorVariationGenerator.VarPatternCount) { break; } } diff --git a/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs index 5b15c90..cbbc2b5 100644 --- a/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs +++ b/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs @@ -5,34 +5,10 @@ namespace BCnEncoder.Encoder { - internal class Bc2BlockEncoder : IBcBlockEncoder + internal class Bc2BlockEncoder : BaseBcBlockEncoder { - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) - { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) - { - Parallel.For(0, blocks.Length, i => - { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else - { - for (var i = 0; i < blocks.Length; i++) - { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc2Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) + protected override Bc2Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { switch (quality) { @@ -48,17 +24,17 @@ private Bc2Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality } } - public GlInternalFormat GetInternalFormat() + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRgbaS3TcDxt3Ext; } - public GlFormat GetBaseInternalFormat() + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRgba; } - public DxgiFormat GetDxgiFormat() { + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc2Unorm; } @@ -120,8 +96,8 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) } private static class Bc2BlockEncoderBalanced { - private const int MaxTries_ = 24 * 2; - private const float ErrorThreshold_ = 0.05f; + private const int MaxTries = 24 * 2; + private const float ErrorThreshold = 0.05f; internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) { @@ -135,7 +111,7 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var best = TryColors(rawBlock, c0, c1, out var bestError); - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); var block = TryColors(rawBlock, newC0, newC1, out var error); @@ -148,7 +124,7 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) c1 = newC1; } - if (bestError < ErrorThreshold_) { + if (bestError < ErrorThreshold) { break; } } @@ -159,8 +135,8 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc2BlockEncoderSlow { - private const int MaxTries_ = 9999; - private const float ErrorThreshold_ = 0.01f; + private const int MaxTries = 9999; + private const float ErrorThreshold = 0.01f; internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) @@ -184,7 +160,7 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var lastChanged = 0; - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (newC0.data < newC1.data) @@ -207,7 +183,7 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) lastChanged = 0; } - if (bestError < ErrorThreshold_ || lastChanged > ColorVariationGenerator.VarPatternCount) { + if (bestError < ErrorThreshold || lastChanged > ColorVariationGenerator.VarPatternCount) { break; } } diff --git a/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs index 04b854f..01b0fc3 100644 --- a/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs +++ b/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs @@ -6,34 +6,9 @@ namespace BCnEncoder.Encoder { - internal class Bc3BlockEncoder : IBcBlockEncoder + internal class Bc3BlockEncoder : BaseBcBlockEncoder { - - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) - { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) - { - Parallel.For(0, blocks.Length, i => - { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else - { - for (var i = 0; i < blocks.Length; i++) - { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc3Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) + protected override Bc3Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { switch (quality) { @@ -49,17 +24,17 @@ private Bc3Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality } } - public GlInternalFormat GetInternalFormat() + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRgbaS3TcDxt5Ext; } - public GlFormat GetBaseInternalFormat() + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRgba; } - public DxgiFormat GetDxgiFormat() { + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc3Unorm; } @@ -286,8 +261,8 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) } private static class Bc3BlockEncoderBalanced { - private const int MaxTries_ = 24 * 2; - private const float ErrorThreshold_ = 0.05f; + private const int MaxTries = 24 * 2; + private const float ErrorThreshold = 0.05f; internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) { @@ -301,7 +276,7 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var best = TryColors(rawBlock, c0, c1, out var bestError); - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); var block = TryColors(rawBlock, newC0, newC1, out var error); @@ -314,7 +289,7 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) c1 = newC1; } - if (bestError < ErrorThreshold_) { + if (bestError < ErrorThreshold) { break; } } @@ -325,8 +300,8 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc3BlockEncoderSlow { - private const int MaxTries_ = 9999; - private const float ErrorThreshold_ = 0.01f; + private const int MaxTries = 9999; + private const float ErrorThreshold = 0.01f; internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) @@ -350,7 +325,7 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) var lastChanged = 0; - for (var i = 0; i < MaxTries_; i++) { + for (var i = 0; i < MaxTries; i++) { var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i); if (newC0.data < newC1.data) @@ -373,7 +348,7 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) lastChanged = 0; } - if (bestError < ErrorThreshold_ || lastChanged > ColorVariationGenerator.VarPatternCount) { + if (bestError < ErrorThreshold || lastChanged > ColorVariationGenerator.VarPatternCount) { break; } } diff --git a/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs index 632238f..a842158 100644 --- a/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs +++ b/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs @@ -6,34 +6,15 @@ namespace BCnEncoder.Encoder { - internal class Bc4BlockEncoder : IBcBlockEncoder { - + internal class Bc4BlockEncoder : BaseBcBlockEncoder + { private readonly bool luminanceAsRed; + public Bc4BlockEncoder(bool luminanceAsRed) { this.luminanceAsRed = luminanceAsRed; } - - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, - bool parallel) { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) { - Parallel.For(0, blocks.Length, i => { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else { - for (var i = 0; i < blocks.Length; i++) { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc4Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { + + protected override Bc4Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { var output = new Bc4Block(); var colors = new byte[16]; var pixels = block.AsSpan; @@ -58,15 +39,15 @@ private Bc4Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality } } - public GlInternalFormat GetInternalFormat() { + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRedRgtc1Ext; } - public GlFormat GetBaseInternalFormat() { + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRed; } - public DxgiFormat GetDxgiFormat() { + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc4Unorm; } diff --git a/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs index 2f86bae..f5ee63d 100644 --- a/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs +++ b/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs @@ -6,29 +6,9 @@ namespace BCnEncoder.Encoder { - internal class Bc5BlockEncoder : IBcBlockEncoder { - - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) - { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) { - Parallel.For(0, blocks.Length, i => { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else { - for (var i = 0; i < blocks.Length; i++) { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } - } - - return outputData; - } - - private Bc5Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { + internal class Bc5BlockEncoder : BaseBcBlockEncoder + { + protected override Bc5Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) { var output = new Bc5Block(); var reds = new byte[16]; var greens = new byte[16]; @@ -100,15 +80,15 @@ private Bc5Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality return output; } - public GlInternalFormat GetInternalFormat() { + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRedGreenRgtc2Ext; } - public GlFormat GetBaseInternalFormat() { + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRg; } - public DxgiFormat GetDxgiFormat() { + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc5Unorm; } diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs index f163c3e..ce3a635 100644 --- a/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs +++ b/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs @@ -6,45 +6,35 @@ namespace BCnEncoder.Encoder.Bc7 { - internal class Bc7Encoder : IBcBlockEncoder + internal class Bc7Encoder : BaseBcBlockEncoder { - public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel) + protected override Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock, CompressionQuality quality) { - var outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()]; - var outputBlocks = MemoryMarshal.Cast(outputData); - - if (parallel) - { - Parallel.For(0, blocks.Length, i => - { - var outputBlocks = MemoryMarshal.Cast(outputData); - outputBlocks[i] = EncodeBlock(blocks[i], quality); - }); - } - else + switch (quality) { - for (var i = 0; i < blocks.Length; i++) - { - outputBlocks[i] = EncodeBlock(blocks[i], quality); - } + case CompressionQuality.Fast: + return Bc7EncoderFast.EncodeBlock(rawBlock); + case CompressionQuality.Balanced: + return Bc7EncoderBalanced.EncodeBlock(rawBlock); + case CompressionQuality.BestQuality: + return Bc7EncoderBestQuality.EncodeBlock(rawBlock); + default: + throw new ArgumentOutOfRangeException(nameof(quality), quality, null); } - - - return outputData; } - public GlInternalFormat GetInternalFormat() + public override GlInternalFormat GetInternalFormat() { return GlInternalFormat.GlCompressedRgbaBptcUnormArb; } - public GlFormat GetBaseInternalFormat() + public override GlFormat GetBaseInternalFormat() { return GlFormat.GlRgba; } - public DxgiFormat GetDxgiFormat() { + public override DxgiFormat GetDxgiFormat() { return DxgiFormat.DxgiFormatBc7Unorm; } @@ -73,26 +63,10 @@ private static ClusterIndices4X4 CreateClusterIndexBlock(RawBlock4X4Rgba32 raw, return indexBlock; } - - private static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock, CompressionQuality quality) - { - switch (quality) - { - case CompressionQuality.Fast: - return Bc7EncoderFast.EncodeBlock(rawBlock); - case CompressionQuality.Balanced: - return Bc7EncoderBalanced.EncodeBlock(rawBlock); - case CompressionQuality.BestQuality: - return Bc7EncoderBestQuality.EncodeBlock(rawBlock); - default: - throw new ArgumentOutOfRangeException(nameof(quality), quality, null); - } - } - private static class Bc7EncoderFast { - private const float ErrorThreshold_ = 0.005f; - private const int MaxTries_ = 5; + private const float ErrorThreshold = 0.005f; + private const int MaxTries = 5; private static IEnumerable TryMethods(RawBlock4X4Rgba32 rawBlock, int[] best2SubsetPartitions, int[] best3SubsetPartitions, bool alpha) { @@ -143,7 +117,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) bestError = error; } - if (error < ErrorThreshold_ || tries > MaxTries_) { + if (error < ErrorThreshold || tries > MaxTries) { break; } @@ -155,8 +129,8 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc7EncoderBalanced { - private const float ErrorThreshold_ = 0.005f; - private const int MaxTries_ = 25; + private const float ErrorThreshold = 0.005f; + private const int MaxTries = 25; private static IEnumerable TryMethods(RawBlock4X4Rgba32 rawBlock, int[] best2SubsetPartitions, int[] best3SubsetPartitions, bool alpha) { @@ -216,7 +190,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) bestError = error; } - if (error < ErrorThreshold_ || tries > MaxTries_) { + if (error < ErrorThreshold || tries > MaxTries) { break; } @@ -229,8 +203,8 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) private static class Bc7EncoderBestQuality { - private const float ErrorThreshold_ = 0.001f; - private const int MaxTries_ = 40; + private const float ErrorThreshold = 0.001f; + private const int MaxTries = 40; private static IEnumerable TryMethods(RawBlock4X4Rgba32 rawBlock, int[] best2SubsetPartitions, int[] best3SubsetPartitions, bool alpha) { @@ -292,7 +266,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 rawBlock) bestError = error; } - if (error < ErrorThreshold_ || tries > MaxTries_) { + if (error < ErrorThreshold || tries > MaxTries) { break; } diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs index 55bf8f6..7505b53 100644 --- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs +++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs @@ -7,7 +7,7 @@ internal static class Bc7Mode4Encoder { private static ReadOnlySpan PartitionTable => new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - const int Subset_ = 0; + const int Subset = 0; public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation) { @@ -32,7 +32,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio byte pBit = 0; //fake pBit Bc7EncodingHelpers.OptimizeSubsetEndpointsWithPBit(type, rotatedBlock, ref scaledEp0, - ref scaledEp1, ref pBit, ref pBit, startingVariation, PartitionTable, Subset_, + ref scaledEp1, ref pBit, ref pBit, startingVariation, PartitionTable, Subset, false, true, idxMode); ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0); diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs index 1d97ac6..0e93a82 100644 --- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs +++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs @@ -6,7 +6,7 @@ namespace BCnEncoder.Encoder.Bc7 internal static class Bc7Mode5Encoder { private static ReadOnlySpan PartitionTable => new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - const int Subset_ = 0; + const int Subset = 0; public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation) { @@ -28,7 +28,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio byte pBit = 0; //fake pBit Bc7EncodingHelpers.OptimizeSubsetEndpointsWithPBit(type, rotatedBlock, ref scaledEp0, - ref scaledEp1, ref pBit, ref pBit, startingVariation, PartitionTable, Subset_, false, true); + ref scaledEp1, ref pBit, ref pBit, startingVariation, PartitionTable, Subset, false, true); ep0 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp0, 0); ep1 = Bc7EncodingHelpers.ExpandEndpoint(type, scaledEp1, 0); diff --git a/BCnEnc.Net/Encoder/BcEncoder.cs b/BCnEnc.Net/Encoder/BcEncoder.cs index 7e4ec08..4db96b9 100644 --- a/BCnEnc.Net/Encoder/BcEncoder.cs +++ b/BCnEnc.Net/Encoder/BcEncoder.cs @@ -361,16 +361,103 @@ public byte[] EncodeToRawBytes(Image inputImage, int mipLevel, out int m return encoded; } + /// + /// Encodes all mipmap levels into a list of byte buffers. + /// + /// The raw pixels of the input image in rgba format. + /// The width of the input image. + /// The height of the input image. + /// A list of raw encoded mipmap data. + public IList EncodeToRawBytes(byte[] inputPixelsRgba, int inputImageWidth, int inputImageHeight) + { + if (inputPixelsRgba.Length != inputImageWidth * inputImageHeight * 4) + { + throw new ArgumentException("The input pixels must be provided in 4 bytes per pixel rgba format."); + } + using var image = Image.LoadPixelData(inputPixelsRgba, inputImageWidth, inputImageHeight); + return EncodeToRawBytes(image); + } + + /// + /// Encodes a single mip level of the input image to a byte buffer. + /// + /// The raw pixels of the input image in rgba format. + /// The width of the input image. + /// The height of the input image. + /// The mipmap to encode. + /// The width of the mipmap. + /// The height of the mipmap. + /// The raw encoded data. + public byte[] EncodeToRawBytes(byte[] inputPixelsRgba, int inputImageWidth, int inputImageHeight, int mipLevel, out int mipWidth, out int mipHeight) + { + if (inputPixelsRgba.Length != inputImageWidth * inputImageHeight * 4) + { + throw new ArgumentException("The input pixels must be provided in 4 bytes per pixel rgba format."); + } + using var image = Image.LoadPixelData(inputPixelsRgba, inputImageWidth, inputImageHeight); + return EncodeToRawBytes(image, mipLevel, out mipWidth, out mipHeight); + } + + /// + /// Calculates the number of mipmap levels that will be generated for the given input image. + /// + /// The image to use for the calculation. + /// The number of mipmap levels that will be generated for the input image. + public int CalculateNumberOfMipLevels(Image inputImage) + { + return (int) MipMapper.CalculateMipChainLength(inputImage.Width, inputImage.Height, + (OutputOptions.GenerateMipMaps ? (uint) OutputOptions.MaxMipMapLevel : 1)); + } + + /// + /// Calculates the number of mipmap levels that will be generated for the given input image. + /// + /// The width of the input image in pixels + /// The height of the input image in pixels + /// The number of mipmap levels that will be generated for the input image. + public int CalculateNumberOfMipLevels(int imagePixelWidth, int imagePixelHeight) + { + return (int)MipMapper.CalculateMipChainLength(imagePixelWidth, imagePixelHeight, + (OutputOptions.GenerateMipMaps ? (uint)OutputOptions.MaxMipMapLevel : 1)); + } + + /// + /// Calculates the size of a given mipmap level. + /// + /// The image to use for the calculation. + /// The mipLevel to calculate (0 is original image) + /// The mipmap width calculated + /// The mipmap height calculated + public void CalculateMipMapSize(Image inputImage, int mipLevel, out int mipWidth, out int mipHeight) + { + MipMapper.CalculateMipLevelSize(inputImage.Width, inputImage.Height, mipLevel, out mipWidth, + out mipHeight); + } + + /// + /// Calculates the size of a given mipmap level. + /// + /// The width of the input image in pixels + /// The height of the input image in pixels + /// The mipLevel to calculate (0 is original image) + /// The mipmap width calculated + /// The mipmap height calculated + public void CalculateMipMapSize(int imagePixelWidth, int imagePixelHeight, int mipLevel, out int mipWidth, out int mipHeight) + { + MipMapper.CalculateMipLevelSize(imagePixelWidth, imagePixelHeight, mipLevel, out mipWidth, + out mipHeight); + } + /// /// Encodes all cubemap faces and mipmap levels into Ktx file and writes it to the output stream. /// Order is +X, -X, +Y, -Y, +Z, -Z /// /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. + /// The left face of the cubemap. + /// The top face of the cubemap. + /// The bottom face of the cubemap. + /// The back face of the cubemap. + /// The front face of the cubemap. /// The stream to write the encoded image to. public void EncodeCubeMap(Image right, Image left, Image top, Image down, Image back, Image front, Stream outputStream) @@ -394,11 +481,11 @@ public void EncodeCubeMap(Image right, Image left, Image /// Order is +X, -X, +Y, -Y, +Z, -Z. Back maps to positive Z and front to negative Z. /// /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. + /// The left face of the cubemap. + /// The top face of the cubemap. + /// The bottom face of the cubemap. + /// The back face of the cubemap. + /// The front face of the cubemap. /// The Ktx file containing the encoded image. public KtxFile EncodeCubeMapToKtx(Image right, Image left, Image top, Image down, Image back, Image front) @@ -507,11 +594,11 @@ public KtxFile EncodeCubeMapToKtx(Image right, Image left, Image /// Order is +X, -X, +Y, -Y, +Z, -Z. Back maps to positive Z and front to negative Z. /// /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. - /// The right face of the cubemap. + /// The left face of the cubemap. + /// The top face of the cubemap. + /// The bottom face of the cubemap. + /// The back face of the cubemap. + /// The front face of the cubemap. /// The Dds file containing the encoded image. public DdsFile EncodeCubeMapToDds(Image right, Image left, Image top, Image down, Image back, Image front) diff --git a/BCnEnc.Net/Encoder/ColorVariationGenerator.cs b/BCnEnc.Net/Encoder/ColorVariationGenerator.cs index d505361..5115361 100644 --- a/BCnEnc.Net/Encoder/ColorVariationGenerator.cs +++ b/BCnEnc.Net/Encoder/ColorVariationGenerator.cs @@ -6,26 +6,26 @@ namespace BCnEncoder.Encoder internal static class ColorVariationGenerator { - private static int[] varPatternEp0R = new int[] { 1, 1, 0, 0, -1, 0, 0, -1, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static int[] varPatternEp0G = new int[] { 1, 0, 1, 0, 0, -1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static int[] varPatternEp0B = new int[] { 1, 0, 0, 1, 0, 0, -1, -1, 1, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0 }; - private static int[] varPatternEp1R = new int[] { -1, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, -1, 0, 0 }; - private static int[] varPatternEp1G = new int[] { -1, 0, -1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, -1, 0 }; - private static int[] varPatternEp1B = new int[] { -1, 0, 0, -1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, -1 }; - public static int VarPatternCount => varPatternEp0R.Length; + private static readonly int[] variatePatternEp0R = new int[] { 1, 1, 0, 0, -1, 0, 0, -1, 1, -1, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + private static readonly int[] variatePatternEp0G = new int[] { 1, 0, 1, 0, 0, -1, 0, -1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + private static readonly int[] variatePatternEp0B = new int[] { 1, 0, 0, 1, 0, 0, -1, -1, 1, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0 }; + private static readonly int[] variatePatternEp1R = new int[] { -1, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, -1, 0, 0 }; + private static readonly int[] variatePatternEp1G = new int[] { -1, 0, -1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 1, 0, 0, -1, 0 }; + private static readonly int[] variatePatternEp1B = new int[] { -1, 0, 0, -1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, -1 }; + public static int VarPatternCount => variatePatternEp0R.Length; public static (ColorRgb565, ColorRgb565) Variate565(ColorRgb565 c0, ColorRgb565 c1, int i) { - var idx = i % varPatternEp0R.Length; + var idx = i % variatePatternEp0R.Length; var newEp0 = new ColorRgb565(); var newEp1 = new ColorRgb565(); - newEp0.RawR = ByteHelper.ClampToByte(c0.RawR + varPatternEp0R[idx]); - newEp0.RawG = ByteHelper.ClampToByte(c0.RawG + varPatternEp0G[idx]); - newEp0.RawB = ByteHelper.ClampToByte(c0.RawB + varPatternEp0B[idx]); + newEp0.RawR = ByteHelper.ClampToByte(c0.RawR + variatePatternEp0R[idx]); + newEp0.RawG = ByteHelper.ClampToByte(c0.RawG + variatePatternEp0G[idx]); + newEp0.RawB = ByteHelper.ClampToByte(c0.RawB + variatePatternEp0B[idx]); - newEp1.RawR = ByteHelper.ClampToByte(c1.RawR + varPatternEp1R[idx]); - newEp1.RawG = ByteHelper.ClampToByte(c1.RawG + varPatternEp1G[idx]); - newEp1.RawB = ByteHelper.ClampToByte(c1.RawB + varPatternEp1B[idx]); + newEp1.RawR = ByteHelper.ClampToByte(c1.RawR + variatePatternEp1R[idx]); + newEp1.RawG = ByteHelper.ClampToByte(c1.RawG + variatePatternEp1G[idx]); + newEp1.RawB = ByteHelper.ClampToByte(c1.RawB + variatePatternEp1B[idx]); return (newEp0, newEp1); } diff --git a/BCnEnc.Net/Encoder/IBcBlockEncoder.cs b/BCnEnc.Net/Encoder/IBcBlockEncoder.cs index add7372..e9f991b 100644 --- a/BCnEnc.Net/Encoder/IBcBlockEncoder.cs +++ b/BCnEnc.Net/Encoder/IBcBlockEncoder.cs @@ -1,6 +1,4 @@ -using System; -using BCnEncoder.Shared; -using SixLabors.ImageSharp.PixelFormats; +using BCnEncoder.Shared; namespace BCnEncoder.Encoder { diff --git a/BCnEnc.Net/Encoder/Options/EncoderOutputOptions.cs b/BCnEnc.Net/Encoder/Options/EncoderOutputOptions.cs index 6839889..8f4cb18 100644 --- a/BCnEnc.Net/Encoder/Options/EncoderOutputOptions.cs +++ b/BCnEnc.Net/Encoder/Options/EncoderOutputOptions.cs @@ -19,7 +19,7 @@ public class EncoderOutputOptions public int MaxMipMapLevel { get; set; } = -1; /// - /// The compression Format to use. Default is BC1. + /// The compression Format to use. Default is Bc1. /// public CompressionFormat Format { get; set; } = CompressionFormat.Bc1; diff --git a/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs b/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs index d81fac1..eccf7e6 100644 --- a/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs +++ b/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs @@ -1,7 +1,6 @@ using System; using System.IO; using System.Runtime.CompilerServices; -using System.Text; namespace BCnEncoder.Shared { diff --git a/BCnEnc.Net/Shared/Colors.cs b/BCnEnc.Net/Shared/Colors.cs index 03a2bc5..41de5dc 100644 --- a/BCnEnc.Net/Shared/Colors.cs +++ b/BCnEnc.Net/Shared/Colors.cs @@ -31,76 +31,76 @@ public override int GetHashCode() return !left.Equals(right); } - private const ushort RedMask_ = 0b11111_000000_00000; - private const int RedShift_ = 11; - private const ushort GreenMask_ = 0b00000_111111_00000; - private const int GreenShift_ = 5; - private const ushort BlueMask_ = 0b00000_000000_11111; + private const ushort RedMask = 0b11111_000000_00000; + private const int RedShift = 11; + private const ushort GreenMask = 0b00000_111111_00000; + private const int GreenShift = 5; + private const ushort BlueMask = 0b00000_000000_11111; public ushort data; public byte R { readonly get { - var r5 = (data & RedMask_) >> RedShift_; + var r5 = (data & RedMask) >> RedShift; return (byte)((r5 << 3) | (r5 >> 2)); } set { var r5 = value >> 3; - data = (ushort)(data & ~RedMask_); - data = (ushort)(data | (r5 << RedShift_)); + data = (ushort)(data & ~RedMask); + data = (ushort)(data | (r5 << RedShift)); } } public byte G { readonly get { - var g6 = (data & GreenMask_) >> GreenShift_; + var g6 = (data & GreenMask) >> GreenShift; return (byte)((g6 << 2) | (g6 >> 4)); } set { var g6 = value >> 2; - data = (ushort)(data & ~GreenMask_); - data = (ushort)(data | (g6 << GreenShift_)); + data = (ushort)(data & ~GreenMask); + data = (ushort)(data | (g6 << GreenShift)); } } public byte B { readonly get { - var b5 = data & BlueMask_; + var b5 = data & BlueMask; return (byte)((b5 << 3) | (b5 >> 2)); } set { var b5 = value >> 3; - data = (ushort)(data & ~BlueMask_); + data = (ushort)(data & ~BlueMask); data = (ushort)(data | b5); } } public int RawR { - readonly get { return (data & RedMask_) >> RedShift_; } + readonly get { return (data & RedMask) >> RedShift; } set { if (value > 31) value = 31; if (value < 0) value = 0; - data = (ushort)(data & ~RedMask_); - data = (ushort)(data | (value << RedShift_)); + data = (ushort)(data & ~RedMask); + data = (ushort)(data | (value << RedShift)); } } public int RawG { - readonly get { return (data & GreenMask_) >> GreenShift_; } + readonly get { return (data & GreenMask) >> GreenShift; } set { if (value > 63) value = 63; if (value < 0) value = 0; - data = (ushort)(data & ~GreenMask_); - data = (ushort)(data | (value << GreenShift_)); + data = (ushort)(data & ~GreenMask); + data = (ushort)(data | (value << GreenShift)); } } public int RawB { - readonly get { return data & BlueMask_; } + readonly get { return data & BlueMask; } set { if (value > 31) value = 31; if (value < 0) value = 0; - data = (ushort)(data & ~BlueMask_); + data = (ushort)(data & ~BlueMask); data = (ushort)(data | value); } } diff --git a/BCnEnc.Net/Shared/ImageToBlocks.cs b/BCnEnc.Net/Shared/ImageToBlocks.cs index 4f3b1e7..6738d4e 100644 --- a/BCnEnc.Net/Shared/ImageToBlocks.cs +++ b/BCnEnc.Net/Shared/ImageToBlocks.cs @@ -1,6 +1,5 @@ using System; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; namespace BCnEncoder.Shared @@ -130,5 +129,12 @@ internal static Image ImageFromRawBlocks(RawBlock4X4Rgba32[] blocks, int return output; } + + public static int CalculateNumOfBlocks(int pixelWidth, int pixelHeight) + { + int blocksWidth = (int)MathF.Ceiling(pixelWidth / 4.0f); + int blocksHeight = (int)MathF.Ceiling(pixelHeight / 4.0f); + return blocksWidth * blocksHeight; + } } } diff --git a/BCnEnc.Net/Shared/MipMapper.cs b/BCnEnc.Net/Shared/MipMapper.cs index 65e3919..7e66fb4 100644 --- a/BCnEnc.Net/Shared/MipMapper.cs +++ b/BCnEnc.Net/Shared/MipMapper.cs @@ -6,7 +6,7 @@ namespace BCnEncoder.Shared { - public static class MipMapper + internal static class MipMapper { public static uint CalculateMipChainLength(int width, int height, uint maxNumMipMaps) { @@ -28,6 +28,25 @@ public static uint CalculateMipChainLength(int width, int height, uint maxNumMip return output; } + public static void CalculateMipLevelSize(int width, int height, int mipIdx, out int mipWidth, out int mipHeight) + { + mipWidth = width; + mipHeight = height; + + if (mipIdx == 0) return; + + for (uint mipLevel = 1; mipLevel < 99999; mipLevel++) + { + mipWidth = Math.Max(1, width / (int)Math.Pow(2, mipLevel)); + mipHeight = Math.Max(1, height / (int)Math.Pow(2, mipLevel)); + if (mipLevel == mipIdx) return; + if (mipWidth == 1 && mipHeight == 1) + { + return; + } + } + } + public static List> GenerateMipChain(Image sourceImage, ref uint numMipMaps) { var result = new List>(); result.Add(sourceImage.Clone()); diff --git a/BCnEnc.Net/Shared/PcaVectors.cs b/BCnEnc.Net/Shared/PcaVectors.cs index cd735f3..b6d9390 100644 --- a/BCnEnc.Net/Shared/PcaVectors.cs +++ b/BCnEnc.Net/Shared/PcaVectors.cs @@ -1,15 +1,13 @@ using System; +using System.Numerics; using SixLabors.ImageSharp.PixelFormats; -using Vector3 = System.Numerics.Vector3; -using Vector4 = System.Numerics.Vector4; -using Matrix4x4 = System.Numerics.Matrix4x4; namespace BCnEncoder.Shared { internal static class PcaVectors { - private const int C5655Mask_ = 0xF8; - private const int C5656Mask_ = 0xFC; + private const int C565_5Mask = 0xF8; + private const int C565_6Mask = 0xFC; private static void ConvertToVector4(ReadOnlySpan colors, Span vectors) { @@ -223,13 +221,13 @@ public static void GetMinMaxColor565(Span colors, Vector3 mean, Vector3 maxB = maxB <= 255 ? maxB : 255; // Optimal round - minR = (minR & C5655Mask_) | (minR >> 5); - minG = (minG & C5656Mask_) | (minG >> 6); - minB = (minB & C5655Mask_) | (minB >> 5); + minR = (minR & C565_5Mask) | (minR >> 5); + minG = (minG & C565_6Mask) | (minG >> 6); + minB = (minB & C565_5Mask) | (minB >> 5); - maxR = (maxR & C5655Mask_) | (maxR >> 5); - maxG = (maxG & C5656Mask_) | (maxG >> 6); - maxB = (maxB & C5655Mask_) | (maxB >> 5); + maxR = (maxR & C565_5Mask) | (maxR >> 5); + maxG = (maxG & C565_6Mask) | (maxG >> 6); + maxB = (maxB & C565_5Mask) | (maxB >> 5); min = new ColorRgb565((byte)minR, (byte)minG, (byte)minB); max = new ColorRgb565((byte)maxR, (byte)maxG, (byte)maxB); diff --git a/BCnEncNet.sln b/BCnEncNet.sln index 96b2490..e93a46c 100644 --- a/BCnEncNet.sln +++ b/BCnEncNet.sln @@ -7,6 +7,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BCnEncoder", "BCnEnc.Net\BC EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BCnEncTests", "BCnEncTests\BCnEncTests.csproj", "{8E5E366B-9782-4194-AC85-602E8387496A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5B2FBB0B-E3B9-44EB-BBE6-E7EC9F3E56AD}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/BCnEncNet.sln.DotSettings b/BCnEncNet.sln.DotSettings new file mode 100644 index 0000000..7ba4779 --- /dev/null +++ b/BCnEncNet.sln.DotSettings @@ -0,0 +1,4 @@ + + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> \ No newline at end of file diff --git a/BCnEncTests/BC1Tests.cs b/BCnEncTests/BC1Tests.cs index 4171d73..1bd4006 100644 --- a/BCnEncTests/BC1Tests.cs +++ b/BCnEncTests/BC1Tests.cs @@ -4,7 +4,7 @@ namespace BCnEncTests { - public class BC1Tests + public class Bc1Tests { [Fact] diff --git a/BCnEncTests/Bc7BlockTests.cs b/BCnEncTests/Bc7BlockTests.cs index d933884..ac0ce90 100644 --- a/BCnEncTests/Bc7BlockTests.cs +++ b/BCnEncTests/Bc7BlockTests.cs @@ -1,10 +1,7 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using System.Text; using BCnEncoder.Shared; using Xunit; diff --git a/BCnEncTests/BlockTests.cs b/BCnEncTests/BlockTests.cs index 2213ed6..df98d80 100644 --- a/BCnEncTests/BlockTests.cs +++ b/BCnEncTests/BlockTests.cs @@ -1,7 +1,6 @@ -using System; +using System; using BCnEncoder.Shared; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using Xunit; diff --git a/BCnEncTests/ClusterTests.cs b/BCnEncTests/ClusterTests.cs index 26e613c..fabaf69 100644 --- a/BCnEncTests/ClusterTests.cs +++ b/BCnEncTests/ClusterTests.cs @@ -1,12 +1,7 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; -using System.Linq; -using System.Text; using BCnEncoder.Shared; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; using Xunit; namespace BCnEncTests @@ -15,7 +10,7 @@ public class ClusterTests { [Fact] public void Clusterize() { - using var testImage = ImageLoader.testBlur1.Clone(); + using var testImage = ImageLoader.TestBlur1.Clone(); if (!testImage.TryGetSinglePixelSpan(out var pixels)) { throw new Exception("Cannot get pixel span."); diff --git a/BCnEncTests/DdsReadTests.cs b/BCnEncTests/DdsReadTests.cs index 4ba15d7..879abe2 100644 --- a/BCnEncTests/DdsReadTests.cs +++ b/BCnEncTests/DdsReadTests.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; -using System.Linq; -using System.Text; using BCnEncoder.Decoder; using BCnEncoder.Shared; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; using Xunit; namespace BCnEncTests @@ -56,7 +51,7 @@ public void ReadBc1() { } [Fact] - public void ReadBc1a() { + public void ReadBc1A() { using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc1a.dds"); DdsFile file = DdsFile.Load(fs); Assert.Equal(DxgiFormat.DxgiFormatBc1Unorm, file.header.ddsPixelFormat.DxgiFormat); diff --git a/BCnEncTests/DdsWritingTests.cs b/BCnEncTests/DdsWritingTests.cs index 590eeb9..a6a12fc 100644 --- a/BCnEncTests/DdsWritingTests.cs +++ b/BCnEncTests/DdsWritingTests.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.IO; -using System.Text; using BCnEncoder.Encoder; using BCnEncoder.Shared; using Xunit; @@ -12,7 +9,7 @@ public class DdsWritingTests { [Fact] public void DdsWriteRgba() { - var image = ImageLoader.testLenna; + var image = ImageLoader.TestLenna; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -27,7 +24,7 @@ public void DdsWriteRgba() { [Fact] public void DdsWriteBc1() { - var image = ImageLoader.testLenna; + var image = ImageLoader.TestLenna; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -42,7 +39,7 @@ public void DdsWriteBc1() { [Fact] public void DdsWriteBc2() { - var image = ImageLoader.testAlpha1; + var image = ImageLoader.TestAlpha1; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -57,7 +54,7 @@ public void DdsWriteBc2() { [Fact] public void DdsWriteBc3() { - var image = ImageLoader.testAlpha1; + var image = ImageLoader.TestAlpha1; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -72,7 +69,7 @@ public void DdsWriteBc3() { [Fact] public void DdsWriteBc4() { - var image = ImageLoader.testHeight1; + var image = ImageLoader.TestHeight1; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -87,7 +84,7 @@ public void DdsWriteBc4() { [Fact] public void DdsWriteBc5() { - var image = ImageLoader.testRedGreen1; + var image = ImageLoader.TestRedGreen1; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -102,7 +99,7 @@ public void DdsWriteBc5() { [Fact] public void DdsWriteBc7() { - var image = ImageLoader.testLenna; + var image = ImageLoader.TestLenna; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; @@ -117,7 +114,7 @@ public void DdsWriteBc7() { [Fact] public void DdsWriteCubemap() { - var images = ImageLoader.testCubemap; + var images = ImageLoader.TestCubemap; BcEncoder encoder = new BcEncoder(); encoder.OutputOptions.Quality = CompressionQuality.Fast; diff --git a/BCnEncTests/DecodingTests.cs b/BCnEncTests/DecodingTests.cs index 9e69aed..d5260fa 100644 --- a/BCnEncTests/DecodingTests.cs +++ b/BCnEncTests/DecodingTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Collections.Generic; using System.IO; using BCnEncoder.Decoder; using BCnEncoder.Shared; diff --git a/BCnEncTests/EncodingTest.cs b/BCnEncTests/EncodingTest.cs index 8c8c4c1..82c5524 100644 --- a/BCnEncTests/EncodingTest.cs +++ b/BCnEncTests/EncodingTest.cs @@ -1,9 +1,6 @@ using System.IO; using BCnEncoder.Encoder; using BCnEncoder.Shared; -using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions; @@ -21,7 +18,7 @@ public Bc1GradientTest(ITestOutputHelper output) [Fact] public void Bc1GradientBestQuality() { - var image = ImageLoader.testGradient1; + var image = ImageLoader.TestGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -33,7 +30,7 @@ public void Bc1GradientBestQuality() [Fact] public void Bc1GradientBalanced() { - var image = ImageLoader.testGradient1; + var image = ImageLoader.TestGradient1; TestHelper.ExecuteEncodingTest(image, @@ -46,7 +43,7 @@ public void Bc1GradientBalanced() [Fact] public void Bc1GradientFast() { - var image = ImageLoader.testGradient1; + var image = ImageLoader.TestGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -70,7 +67,7 @@ public Bc1DiffuseTest(ITestOutputHelper output) [Fact] public void Bc1DiffuseBestQuality() { - var image = ImageLoader.testDiffuse1; + var image = ImageLoader.TestDiffuse1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -82,7 +79,7 @@ public void Bc1DiffuseBestQuality() [Fact] public void Bc1DiffuseBalanced() { - var image = ImageLoader.testDiffuse1; + var image = ImageLoader.TestDiffuse1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -94,7 +91,7 @@ public void Bc1DiffuseBalanced() [Fact] public void Bc1DiffuseFast() { - var image = ImageLoader.testDiffuse1; + var image = ImageLoader.TestDiffuse1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -119,7 +116,7 @@ public Bc1BlurryTest(ITestOutputHelper output) [Fact] public void Bc1BlurBestQuality() { - var image = ImageLoader.testBlur1; + var image = ImageLoader.TestBlur1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -131,7 +128,7 @@ public void Bc1BlurBestQuality() [Fact] public void Bc1BlurBalanced() { - var image = ImageLoader.testBlur1; + var image = ImageLoader.TestBlur1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -143,7 +140,7 @@ public void Bc1BlurBalanced() [Fact] public void Bc1BlurFast() { - var image = ImageLoader.testBlur1; + var image = ImageLoader.TestBlur1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1, @@ -166,9 +163,9 @@ public Bc1ASpriteTest(ITestOutputHelper output) [Fact] - public void Bc1aSpriteBestQuality() + public void Bc1ASpriteBestQuality() { - var image = ImageLoader.testTransparentSprite1; + var image = ImageLoader.TestTransparentSprite1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1WithAlpha, @@ -178,9 +175,9 @@ public void Bc1aSpriteBestQuality() } [Fact] - public void Bc1aSpriteBalanced() + public void Bc1ASpriteBalanced() { - var image = ImageLoader.testTransparentSprite1; + var image = ImageLoader.TestTransparentSprite1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1WithAlpha, @@ -190,9 +187,9 @@ public void Bc1aSpriteBalanced() } [Fact] - public void Bc1aSpriteFast() + public void Bc1ASpriteFast() { - var image = ImageLoader.testTransparentSprite1; + var image = ImageLoader.TestTransparentSprite1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc1WithAlpha, @@ -217,7 +214,7 @@ public Bc2GradientTest(ITestOutputHelper output) [Fact] public void Bc2GradientBestQuality() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc2, @@ -229,7 +226,7 @@ public void Bc2GradientBestQuality() [Fact] public void Bc2GradientBalanced() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc2, @@ -241,7 +238,7 @@ public void Bc2GradientBalanced() [Fact] public void Bc2GradientFast() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc2, @@ -266,7 +263,7 @@ public Bc3GradientTest(ITestOutputHelper output) [Fact] public void Bc3GradientBestQuality() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc3, @@ -278,7 +275,7 @@ public void Bc3GradientBestQuality() [Fact] public void Bc3GradientBalanced() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc3, @@ -290,7 +287,7 @@ public void Bc3GradientBalanced() [Fact] public void Bc3GradientFast() { - var image = ImageLoader.testAlphaGradient1; + var image = ImageLoader.TestAlphaGradient1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc3, @@ -315,7 +312,7 @@ public Bc4RedTest(ITestOutputHelper output) [Fact] public void Bc4RedBestQuality() { - var image = ImageLoader.testHeight1; + var image = ImageLoader.TestHeight1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc4, @@ -327,7 +324,7 @@ public void Bc4RedBestQuality() [Fact] public void Bc4RedBalanced() { - var image = ImageLoader.testHeight1; + var image = ImageLoader.TestHeight1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc4, @@ -339,7 +336,7 @@ public void Bc4RedBalanced() [Fact] public void Bc4RedFast() { - var image = ImageLoader.testHeight1; + var image = ImageLoader.TestHeight1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc4, @@ -364,7 +361,7 @@ public Bc5RedGreenTest(ITestOutputHelper output) [Fact] public void Bc5RedGreenBestQuality() { - var image = ImageLoader.testRedGreen1; + var image = ImageLoader.TestRedGreen1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc5, @@ -376,7 +373,7 @@ public void Bc5RedGreenBestQuality() [Fact] public void Bc5RedGreenBalanced() { - var image = ImageLoader.testRedGreen1; + var image = ImageLoader.TestRedGreen1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc5, @@ -388,7 +385,7 @@ public void Bc5RedGreenBalanced() [Fact] public void Bc5RedGreenFast() { - var image = ImageLoader.testRedGreen1; + var image = ImageLoader.TestRedGreen1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc5, @@ -412,7 +409,7 @@ public Bc7RgbTest(ITestOutputHelper output) [Fact] public void Bc7RgbBestQuality() { - var image = ImageLoader.testRgbHard1; + var image = ImageLoader.TestRgbHard1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -424,7 +421,7 @@ public void Bc7RgbBestQuality() [Fact] public void Bc7RgbBalanced() { - var image = ImageLoader.testRgbHard1; + var image = ImageLoader.TestRgbHard1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -436,7 +433,7 @@ public void Bc7RgbBalanced() [Fact] public void Bc7LennaBalanced() { - var image = ImageLoader.testLenna; + var image = ImageLoader.TestLenna; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -448,7 +445,7 @@ public void Bc7LennaBalanced() [Fact] public void Bc7RgbFast() { - var image = ImageLoader.testRgbHard1; + var image = ImageLoader.TestRgbHard1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -472,7 +469,7 @@ public Bc7RgbaTest(ITestOutputHelper output) [Fact] public void Bc7RgbaBestQuality() { - var image = ImageLoader.testAlpha1; + var image = ImageLoader.TestAlpha1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -484,7 +481,7 @@ public void Bc7RgbaBestQuality() [Fact] public void Bc7RgbaBalanced() { - var image = ImageLoader.testAlpha1; + var image = ImageLoader.TestAlpha1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -496,7 +493,7 @@ public void Bc7RgbaBalanced() [Fact] public void Bc7RgbaFast() { - var image = ImageLoader.testAlpha1; + var image = ImageLoader.TestAlpha1; TestHelper.ExecuteEncodingTest(image, CompressionFormat.Bc7, @@ -512,7 +509,7 @@ public class CubemapTest [Fact] public void WriteCubeMapFile() { - var images = ImageLoader.testCubemap; + var images = ImageLoader.TestCubemap; string filename = "encoding_bc1_cubemap.ktx"; diff --git a/BCnEncTests/ImageLoader.cs b/BCnEncTests/ImageLoader.cs index c840eb7..5008ef3 100644 --- a/BCnEncTests/ImageLoader.cs +++ b/BCnEncTests/ImageLoader.cs @@ -1,22 +1,22 @@ -using SixLabors.ImageSharp; +using SixLabors.ImageSharp; using SixLabors.ImageSharp.PixelFormats; namespace BCnEncTests { public static class ImageLoader { - public static Image testDiffuse1 { get; } = LoadTestImage("../../../testImages/test_diffuse_1_512.jpg"); - public static Image testBlur1 { get; } = LoadTestImage("../../../testImages/test_blur_1_512.jpg"); - public static Image testNormal1 { get; } = LoadTestImage("../../../testImages/test_normal_1_512.jpg"); - public static Image testHeight1 { get; } = LoadTestImage("../../../testImages/test_height_1_512.jpg"); - public static Image testGradient1 { get; } = LoadTestImage("../../../testImages/test_gradient_1_512.jpg"); - public static Image testTransparentSprite1 { get; } = LoadTestImage("../../../testImages/test_transparent.png"); - public static Image testAlphaGradient1 { get; } = LoadTestImage("../../../testImages/test_alphagradient_1_512.png"); - public static Image testAlpha1 { get; } = LoadTestImage("../../../testImages/test_alpha_1_512.png"); - public static Image testRedGreen1 { get; } = LoadTestImage("../../../testImages/test_red_green_1_64.png"); - public static Image testRgbHard1 { get; } = LoadTestImage("../../../testImages/test_rgb_hard_1.png"); - public static Image testLenna { get; } = LoadTestImage("../../../testImages/test_lenna_512.png"); + public static Image TestDiffuse1 { get; } = LoadTestImage("../../../testImages/test_diffuse_1_512.jpg"); + public static Image TestBlur1 { get; } = LoadTestImage("../../../testImages/test_blur_1_512.jpg"); + public static Image TestNormal1 { get; } = LoadTestImage("../../../testImages/test_normal_1_512.jpg"); + public static Image TestHeight1 { get; } = LoadTestImage("../../../testImages/test_height_1_512.jpg"); + public static Image TestGradient1 { get; } = LoadTestImage("../../../testImages/test_gradient_1_512.jpg"); + public static Image TestTransparentSprite1 { get; } = LoadTestImage("../../../testImages/test_transparent.png"); + public static Image TestAlphaGradient1 { get; } = LoadTestImage("../../../testImages/test_alphagradient_1_512.png"); + public static Image TestAlpha1 { get; } = LoadTestImage("../../../testImages/test_alpha_1_512.png"); + public static Image TestRedGreen1 { get; } = LoadTestImage("../../../testImages/test_red_green_1_64.png"); + public static Image TestRgbHard1 { get; } = LoadTestImage("../../../testImages/test_rgb_hard_1.png"); + public static Image TestLenna { get; } = LoadTestImage("../../../testImages/test_lenna_512.png"); - public static Image[] testCubemap { get; } = new [] { + public static Image[] TestCubemap { get; } = new [] { LoadTestImage("../../../testImages/cubemap/right.png"), LoadTestImage("../../../testImages/cubemap/left.png"), LoadTestImage("../../../testImages/cubemap/top.png"), diff --git a/BCnEncTests/PcaTests.cs b/BCnEncTests/PcaTests.cs index a3b500a..7ca0e18 100644 --- a/BCnEncTests/PcaTests.cs +++ b/BCnEncTests/PcaTests.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System; using BCnEncoder.Shared; using Xunit; using Vector4 = System.Numerics.Vector4; diff --git a/BCnEncTests/RawTests.cs b/BCnEncTests/RawTests.cs index e5dd478..0344ec5 100644 --- a/BCnEncTests/RawTests.cs +++ b/BCnEncTests/RawTests.cs @@ -1,38 +1,107 @@ -using System.IO; +using System.IO; using BCnEncoder.Decoder; using BCnEncoder.Encoder; using BCnEncoder.Shared; +using SixLabors.ImageSharp.Processing; using Xunit; namespace BCnEncTests { - public class RawTests - { - [Fact] - public void Decode() - { - using var fs = File.OpenRead(@"../../../testImages/test_decompress_bc1.ktx"); - var ktx = KtxFile.Load(fs); - var decoder = new BcDecoder(); - var encoder = new BcEncoder(); - - var originalImage = decoder.Decode(ktx); - - var rawBytes = encoder.EncodeToRawBytes(originalImage); - var recodedImage = decoder.DecodeRaw(rawBytes[0], CompressionFormat.Bc1, originalImage.Width, originalImage.Height); - - originalImage.TryGetSinglePixelSpan(out var originalPixels); - recodedImage.TryGetSinglePixelSpan(out var recodedPixels); - - var psnr=ImageQuality.PeakSignalToNoiseRatio(originalPixels, recodedPixels); - if (encoder.OutputOptions.Quality == CompressionQuality.Fast) - { - Assert.True(psnr > 25); - } - else - { - Assert.True(psnr > 30); - } - } - } + public class RawTests + { + [Fact] + public void EncodeDecode() + { + var inputImage = ImageLoader.TestGradient1; + var decoder = new BcDecoder(); + var encoder = new BcEncoder() + { + OutputOptions = { Quality = CompressionQuality.BestQuality } + }; + + var encodedRawBytes = encoder.EncodeToRawBytes(inputImage); + var decodedImage = decoder.DecodeRaw(encodedRawBytes[0], CompressionFormat.Bc1, inputImage.Width, inputImage.Height); + + ImageLoader.TestGradient1.TryGetSinglePixelSpan(out var originalPixels); + decodedImage.TryGetSinglePixelSpan(out var decodedPixels); + + var psnr = ImageQuality.PeakSignalToNoiseRatio(originalPixels, decodedPixels); + + Assert.True(psnr > 30); + } + + [Fact] + public void EncodeDecodeStream() + { + var inputImage = ImageLoader.TestGradient1; + var decoder = new BcDecoder(); + var encoder = new BcEncoder() + { + OutputOptions = { Quality = CompressionQuality.BestQuality } + }; + + var encodedRawBytes = encoder.EncodeToRawBytes(inputImage); + + using MemoryStream ms = new MemoryStream(encodedRawBytes[0]); + + Assert.Equal(0, ms.Position); + + var decodedImage = decoder.DecodeRaw(ms, CompressionFormat.Bc1, inputImage.Width, inputImage.Height); + + inputImage.TryGetSinglePixelSpan(out var originalPixels); + decodedImage.TryGetSinglePixelSpan(out var decodedPixels); + + var psnr = ImageQuality.PeakSignalToNoiseRatio(originalPixels, decodedPixels); + + Assert.True(psnr > 30); + } + + + [Fact] + public void EncodeDecodeAllMipMapsStream() + { + var inputImage = ImageLoader.TestGradient1; + var decoder = new BcDecoder(); + var encoder = new BcEncoder + { + OutputOptions = + { + Quality = CompressionQuality.BestQuality, + GenerateMipMaps = true, + MaxMipMapLevel = 0 + } + }; + + using MemoryStream ms = new MemoryStream(); + + var encodedRawBytes = encoder.EncodeToRawBytes(inputImage); + + int mipLevels = encoder.CalculateNumberOfMipLevels(inputImage); + Assert.True(mipLevels > 1); + + for (int i = 0; i < mipLevels; i++) + { + ms.Write(encodedRawBytes[i]); + } + + ms.Position = 0; + Assert.Equal(0, ms.Position); + + for (int i = 0; i < mipLevels; i++) + { + encoder.CalculateMipMapSize(inputImage, i, out int mipWidth, out int mipHeight); + using var resized = inputImage.Clone(x => x.Resize(mipWidth, mipHeight)); + + var decodedImage = decoder.DecodeRaw(ms, CompressionFormat.Bc1, mipWidth, mipHeight); + resized.TryGetSinglePixelSpan(out var originalPixels); + decodedImage.TryGetSinglePixelSpan(out var decodedPixels); + var psnr = ImageQuality.PeakSignalToNoiseRatio(originalPixels, decodedPixels); + Assert.True(psnr > 30); + } + + encoder.CalculateMipMapSize(inputImage, mipLevels - 1, out int lastMWidth, out int lastMHeight); + Assert.Equal(1, lastMWidth); + Assert.Equal(1, lastMWidth); + } + } } diff --git a/BCnEncTests/TestHelper.cs b/BCnEncTests/TestHelper.cs index e08e2db..4d23ce8 100644 --- a/BCnEncTests/TestHelper.cs +++ b/BCnEncTests/TestHelper.cs @@ -1,12 +1,9 @@ -using System; -using System.Collections.Generic; +using System; using System.IO; -using System.Text; using BCnEncoder.Decoder; using BCnEncoder.Encoder; using BCnEncoder.Shared; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; using Xunit; using Xunit.Abstractions;