diff --git a/BCnEnc.Net/BCnEncoder.csproj b/BCnEnc.Net/BCnEncoder.csproj
index da61c8d..a1b7e6a 100644
--- a/BCnEnc.Net/BCnEncoder.csproj
+++ b/BCnEnc.Net/BCnEncoder.csproj
@@ -1,37 +1,31 @@
- netstandard2.1
+ netstandard1.1;net45
true
true
snupkg
true
- bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml
+ bin\Debug\netstandard1.1\BCnEncoderNet45.xml
MIT OR Unlicense
false
- 1.2.3
+ 0.0.1
Nominom
- BCnEncoder.Net
- BCnEncoder.NET is a library for compressing rgba images to different block-compressed formats. Both ktx and dds output formats are supported. It has no native dependencies and is .NET Standard 2.1 compatible.
-
-Supported formats are:
- Raw unsigned byte R, RG, RGB and RGBA formats
- BC1 (S3TC DXT1)
- BC2 (S3TC DXT3)
- BC3 (S3TC DXT5)
- BC4 (RGTC1)
- BC5 (RGTC2)
- BC7 (BPTC)
- BCnEncoder.Net
+ BCnEncoder.Net45
+ BCnEncoder.NET is a library for compressing rgba images to different block-compressed formats. Both ktx and dds output formats are supported.
+This version is a .NET Framework 4.5 compatible legacy version. If you're using .NET Core, please use the main package instead.
+
+ BCnEncoder.Net45
https://github.com/Nominom/BCnEncoder.NET
git
BCn BC BC1 BC2 BC3 BC4 BC5 BC7 BPTC RGTC S3TC DXT1 DXT3 DXT5 ktx dds texture compression encoding decoding decompression image gpu
https://github.com/Nominom/BCnEncoder.NET
- Upgraded ImageSharp dependency from 1.0.0 beta 7 to version 1.0.1
+ Initial .Net Framework 4.5 support version.
+ BCnEncoderNet45
@@ -44,8 +38,8 @@ Supported formats are:
-
+
diff --git a/BCnEnc.Net/Decoder/Bc7Decoder.cs b/BCnEnc.Net/Decoder/Bc7Decoder.cs
index 11a72b6..47c8d10 100644
--- a/BCnEnc.Net/Decoder/Bc7Decoder.cs
+++ b/BCnEnc.Net/Decoder/Bc7Decoder.cs
@@ -7,26 +7,30 @@ namespace BCnEncoder.Decoder
{
internal class Bc7Decoder : IBcBlockDecoder
{
- 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);
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight) {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc7Block))) {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- var rawBlock = encodedBlocks[x + y * blockWidth];
- output[x, y] = rawBlock.Decode();
+ fixed (byte* iDataBytes = data)
+ {
+ Bc7Block* encodedBlocks = (Bc7Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ }
}
}
- return output;
+ return output;
}
}
}
diff --git a/BCnEnc.Net/Decoder/BcBlockDecoder.cs b/BCnEnc.Net/Decoder/BcBlockDecoder.cs
index 5bfa778..110fed4 100644
--- a/BCnEnc.Net/Decoder/BcBlockDecoder.cs
+++ b/BCnEnc.Net/Decoder/BcBlockDecoder.cs
@@ -5,27 +5,36 @@
namespace BCnEncoder.Decoder
{
- internal interface IBcBlockDecoder {
- RawBlock4X4Rgba32[,] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight, out int blockWidth,
+ internal interface IBcBlockDecoder
+ {
+ RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth,
out int blockHeight);
}
- internal class Bc1NoAlphaDecoder : IBcBlockDecoder {
- 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);
+ internal class Bc1NoAlphaDecoder : IBcBlockDecoder
+ {
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc1Block)))
+ {
throw new InvalidDataException();
}
- var encodedBlocks = MemoryMarshal.Cast(data);
-
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode(false);
+ fixed (byte* iDataBytes = data)
+ {
+ Bc1Block* encodedBlocks = (Bc1Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode(false);
+ }
}
}
@@ -33,22 +42,30 @@ internal class Bc1NoAlphaDecoder : IBcBlockDecoder {
}
}
- internal class Bc1ADecoder : IBcBlockDecoder {
- 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);
+ internal class Bc1ADecoder : IBcBlockDecoder
+ {
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc1Block)))
+ {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode(true);
+ fixed (byte* iDataBytes = data)
+ {
+ Bc1Block* encodedBlocks = (Bc1Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode(true);
+ }
}
}
@@ -56,22 +73,30 @@ internal class Bc1ADecoder : IBcBlockDecoder {
}
}
- internal class Bc2Decoder : IBcBlockDecoder {
- 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);
+ internal class Bc2Decoder : IBcBlockDecoder
+ {
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc2Block)))
+ {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ fixed (byte* iDataBytes = data)
+ {
+ Bc2Block* encodedBlocks = (Bc2Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ }
}
}
@@ -79,22 +104,30 @@ internal class Bc2Decoder : IBcBlockDecoder {
}
}
- internal class Bc3Decoder : IBcBlockDecoder {
- 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);
+ internal class Bc3Decoder : IBcBlockDecoder
+ {
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc3Block)))
+ {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ fixed (byte* iDataBytes = data)
+ {
+ Bc3Block* encodedBlocks = (Bc3Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ }
}
}
@@ -102,27 +135,36 @@ internal class Bc3Decoder : IBcBlockDecoder {
}
}
- internal class Bc4Decoder : IBcBlockDecoder {
+ internal class Bc4Decoder : IBcBlockDecoder
+ {
private readonly bool redAsLuminance;
- public Bc4Decoder(bool redAsLuminance) {
+ public Bc4Decoder(bool redAsLuminance)
+ {
this.redAsLuminance = redAsLuminance;
}
- 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);
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc4Block)))
+ {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode(redAsLuminance);
+ fixed (byte* iDataBytes = data)
+ {
+ Bc4Block* encodedBlocks = (Bc4Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode(redAsLuminance);
+ }
}
}
@@ -130,23 +172,31 @@ public Bc4Decoder(bool redAsLuminance) {
}
}
- internal class Bc5Decoder : IBcBlockDecoder {
+ internal class Bc5Decoder : IBcBlockDecoder
+ {
- 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);
+ public unsafe RawBlock4X4Rgba32[,] Decode(byte[] data, int pixelWidth, int pixelHeight, out int blockWidth, out int blockHeight)
+ {
+ blockWidth = (int)Math.Ceiling(pixelWidth / 4.0f);
+ blockHeight = (int)Math.Ceiling(pixelHeight / 4.0f);
- if (data.Length != (blockWidth * blockHeight * Marshal.SizeOf())) {
+ if (data.Length != (blockWidth * blockHeight * sizeof(Bc5Block)))
+ {
throw new InvalidDataException();
}
-
- var encodedBlocks = MemoryMarshal.Cast(data);
-
+
RawBlock4X4Rgba32[,] output = new RawBlock4X4Rgba32[blockWidth, blockHeight];
- for (int x = 0; x < blockWidth; x++) {
- for (int y = 0; y < blockHeight; y++) {
- output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ fixed (byte* iDataBytes = data)
+ {
+ Bc5Block* encodedBlocks = (Bc5Block*)iDataBytes;
+
+ for (int x = 0; x < blockWidth; x++)
+ {
+ for (int y = 0; y < blockHeight; y++)
+ {
+ output[x, y] = encodedBlocks[x + y * blockWidth].Decode();
+ }
}
}
diff --git a/BCnEnc.Net/Decoder/BcDecoder.cs b/BCnEnc.Net/Decoder/BcDecoder.cs
index d768a85..2e55b11 100644
--- a/BCnEnc.Net/Decoder/BcDecoder.cs
+++ b/BCnEnc.Net/Decoder/BcDecoder.cs
@@ -2,13 +2,11 @@
using System.IO;
using System.Text;
using BCnEncoder.Shared;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Decoder
{
- public class DecoderInputOptions {
+ public class DecoderInputOptions
+ {
///
/// The DDS file format doesn't seem to have a standard for indicating whether a BC1 texture
/// includes 1bit of alpha. This option will assume that all Bc1 textures contain alpha.
@@ -18,7 +16,8 @@ public class DecoderInputOptions {
public bool ddsBc1ExpectAlpha = true;
}
- public class DecoderOutputOptions {
+ public class DecoderOutputOptions
+ {
///
/// If true, when decoding from a format that only includes a red channel,
/// output pixels will have all colors set to the same value (greyscale). Default is true.
@@ -26,6 +25,13 @@ public class DecoderOutputOptions {
public bool redAsLuminance = true;
}
+ public struct DecodedMipMap
+ {
+ public int Width;
+ public int Height;
+ public byte[] data;
+ }
+
///
/// Decodes compressed files into Rgba format.
///
@@ -87,6 +93,29 @@ private IBcBlockDecoder GetDecoder(GlInternalFormat format)
}
}
+ private IBcBlockDecoder GetDecoder(CompressionFormat format)
+ {
+ switch (format)
+ {
+ case CompressionFormat.BC1:
+ return new Bc1NoAlphaDecoder();
+ case CompressionFormat.BC1WithAlpha:
+ return new Bc1ADecoder();
+ case CompressionFormat.BC2:
+ return new Bc2Decoder();
+ case CompressionFormat.BC3:
+ return new Bc3Decoder();
+ case CompressionFormat.BC4:
+ return new Bc4Decoder(OutputOptions.redAsLuminance);
+ case CompressionFormat.BC5:
+ return new Bc5Decoder();
+ case CompressionFormat.BC7:
+ return new Bc7Decoder();
+ default:
+ return null;
+ }
+ }
+
private IBcBlockDecoder GetDecoder(DXGI_FORMAT format, DdsHeader header)
{
switch (format)
@@ -94,16 +123,19 @@ private IBcBlockDecoder GetDecoder(DXGI_FORMAT format, DdsHeader header)
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM:
case DXGI_FORMAT.DXGI_FORMAT_BC1_UNORM_SRGB:
case DXGI_FORMAT.DXGI_FORMAT_BC1_TYPELESS:
- if ((header.ddsPixelFormat.dwFlags & PixelFormatFlags.DDPF_ALPHAPIXELS) != 0) {
+ if ((header.ddsPixelFormat.dwFlags & PixelFormatFlags.DDPF_ALPHAPIXELS) != 0)
+ {
return new Bc1ADecoder();
}
- else if(InputOptions.ddsBc1ExpectAlpha){
+ else if (InputOptions.ddsBc1ExpectAlpha)
+ {
return new Bc1ADecoder();
}
- else {
+ else
+ {
return new Bc1NoAlphaDecoder();
}
-
+
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM:
case DXGI_FORMAT.DXGI_FORMAT_BC2_UNORM_SRGB:
case DXGI_FORMAT.DXGI_FORMAT_BC2_TYPELESS:
@@ -164,41 +196,37 @@ private IRawDecoder GetRawDecoder(DXGI_FORMAT format)
///
/// Read a Ktx or a Dds file from a stream and decode it.
///
- public Image Decode(Stream inputStream) {
+ public DecodedMipMap Decode(Stream inputStream)
+ {
var position = inputStream.Position;
- try {
- if (inputStream is FileStream fs) {
- var extension = Path.GetExtension(fs.Name).ToLower();
- if (extension == ".ktx") {
- KtxFile file = KtxFile.Load(inputStream);
- return Decode(file);
- }
- else if (extension == ".dds") {
- DdsFile file = DdsFile.Load(inputStream);
- return Decode(file);
- }
- }
+ try
+ {
bool isDDS = false;
- using (var br = new BinaryReader(inputStream, Encoding.UTF8, true)) {
+ using (var br = new BinaryReader(inputStream, Encoding.UTF8, true))
+ {
var magic = br.ReadUInt32();
- if (magic == 0x20534444U) {
+ if (magic == 0x20534444U)
+ {
isDDS = true;
}
}
inputStream.Seek(position, SeekOrigin.Begin);
- if (isDDS) {
+ if (isDDS)
+ {
DdsFile dds = DdsFile.Load(inputStream);
return Decode(dds);
}
- else {
+ else
+ {
KtxFile ktx = KtxFile.Load(inputStream);
return Decode(ktx);
}
}
- catch (Exception) {
+ catch (Exception)
+ {
inputStream.Seek(position, SeekOrigin.Begin);
throw;
}
@@ -207,41 +235,37 @@ public Image Decode(Stream inputStream) {
///
/// Read a Ktx or a Dds file from a stream and decode it.
///
- public Image[] DecodeAllMipMaps(Stream inputStream) {
+ public DecodedMipMap[] DecodeAllMipMaps(Stream inputStream)
+ {
var position = inputStream.Position;
- try {
- if (inputStream is FileStream fs) {
- var extension = Path.GetExtension(fs.Name).ToLower();
- if (extension == ".ktx") {
- KtxFile file = KtxFile.Load(inputStream);
- return DecodeAllMipMaps(file);
- }
- else if (extension == ".dds") {
- DdsFile file = DdsFile.Load(inputStream);
- return DecodeAllMipMaps(file);
- }
- }
+ try
+ {
bool isDDS = false;
- using (var br = new BinaryReader(inputStream, Encoding.UTF8, true)) {
+ using (var br = new BinaryReader(inputStream, Encoding.UTF8, true))
+ {
var magic = br.ReadUInt32();
- if (magic == 0x20534444U) {
+ if (magic == 0x20534444U)
+ {
isDDS = true;
}
}
inputStream.Seek(position, SeekOrigin.Begin);
- if (isDDS) {
+ if (isDDS)
+ {
DdsFile dds = DdsFile.Load(inputStream);
return DecodeAllMipMaps(dds);
}
- else {
+ else
+ {
KtxFile ktx = KtxFile.Load(inputStream);
return DecodeAllMipMaps(ktx);
}
}
- catch (Exception) {
+ catch (Exception)
+ {
inputStream.Seek(position, SeekOrigin.Begin);
throw;
}
@@ -250,24 +274,35 @@ public Image[] DecodeAllMipMaps(Stream inputStream) {
///
/// Read a KtxFile and decode it.
///
- public Image Decode(KtxFile file)
+ public DecodedMipMap Decode(KtxFile file)
{
- if (IsSupportedRawFormat(file.Header.GlInternalFormat)) {
+ if (IsSupportedRawFormat(file.Header.GlInternalFormat))
+ {
var decoder = GetRawDecoder(file.Header.GlInternalFormat);
var data = file.MipMaps[0].Faces[0].Data;
var pixelWidth = file.MipMaps[0].Width;
var pixelHeight = file.MipMaps[0].Height;
- var image = new Image((int)pixelWidth, (int)pixelHeight);
+ var image = new byte[(int)pixelWidth * (int)pixelHeight * 4];
var output = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight);
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
+
+ for (int i = 0; i < output.Length; i++)
+ {
+ image[i * 4 + 0] = output[i].R;
+ image[i * 4 + 1] = output[i].G;
+ image[i * 4 + 2] = output[i].B;
+ image[i * 4 + 3] = output[i].A;
}
- output.CopyTo(pixels);
- return image;
+ return new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
- else {
+ else
+ {
var decoder = GetDecoder(file.Header.GlInternalFormat);
if (decoder == null)
{
@@ -280,57 +315,80 @@ public Image Decode(KtxFile file)
var blocks = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight, out var blockWidth, out var blockHeight);
- return ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
- (int)pixelWidth, (int)pixelHeight);
+ return new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
+ (int)pixelWidth, (int)pixelHeight)
+ };
}
}
///
/// Read a KtxFile and decode it.
///
- public Image[] DecodeAllMipMaps(KtxFile file)
+ public DecodedMipMap[] DecodeAllMipMaps(KtxFile file)
{
- if (IsSupportedRawFormat(file.Header.GlInternalFormat)) {
+ if (IsSupportedRawFormat(file.Header.GlInternalFormat))
+ {
var decoder = GetRawDecoder(file.Header.GlInternalFormat);
- var images = new Image[file.MipMaps.Count];
+ var images = new DecodedMipMap[file.MipMaps.Count];
- for (int mip = 0; mip < file.MipMaps.Count; mip++) {
+ for (int mip = 0; mip < file.MipMaps.Count; mip++)
+ {
var data = file.MipMaps[mip].Faces[0].Data;
var pixelWidth = file.MipMaps[mip].Width;
var pixelHeight = file.MipMaps[mip].Height;
- var image = new Image((int)pixelWidth, (int)pixelHeight);
+ var image = new byte[(int)pixelWidth * (int)pixelHeight * 4];
var output = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight);
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- output.CopyTo(pixels);
- images[mip] = image;
+ for (int i = 0; i < output.Length; i++)
+ {
+ image[i * 4 + 0] = output[i].R;
+ image[i * 4 + 1] = output[i].G;
+ image[i * 4 + 2] = output[i].B;
+ image[i * 4 + 3] = output[i].A;
+ }
+
+ images[mip] = new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
-
+
return images;
}
- else {
+ else
+ {
var decoder = GetDecoder(file.Header.GlInternalFormat);
if (decoder == null)
{
throw new NotSupportedException($"This format is not supported: {file.Header.GlInternalFormat}");
}
- var images = new Image[file.MipMaps.Count];
+ var images = new DecodedMipMap[file.MipMaps.Count];
- for (int mip = 0; mip < file.MipMaps.Count; mip++) {
+ for (int mip = 0; mip < file.MipMaps.Count; mip++)
+ {
var data = file.MipMaps[mip].Faces[0].Data;
var pixelWidth = file.MipMaps[mip].Width;
var pixelHeight = file.MipMaps[mip].Height;
- var blocks = decoder.Decode(data, (int) pixelWidth, (int) pixelHeight, out var blockWidth,
+ var blocks = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight, out var blockWidth,
out var blockHeight);
- var image = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
+ var image = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
(int)pixelWidth, (int)pixelHeight);
- images[mip] = image;
+ images[mip] = new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
return images;
}
@@ -339,33 +397,46 @@ public Image[] DecodeAllMipMaps(KtxFile file)
///
/// Read a DdsFile and decode it
///
- public Image Decode(DdsFile file)
+ public DecodedMipMap Decode(DdsFile file)
{
- if (IsSupportedRawFormat(file.Header.ddsPixelFormat.DxgiFormat)) {
+ if (IsSupportedRawFormat(file.Header.ddsPixelFormat.DxgiFormat))
+ {
var decoder = GetRawDecoder(file.Header.ddsPixelFormat.DxgiFormat);
var data = file.Faces[0].MipMaps[0].Data;
var pixelWidth = file.Faces[0].Width;
var pixelHeight = file.Faces[0].Height;
- var image = new Image((int)pixelWidth, (int)pixelHeight);
+ var image = new byte[(int)pixelWidth * (int)pixelHeight * 4];
var output = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight);
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- output.CopyTo(pixels);
- return image;
+ for (int i = 0; i < output.Length; i++)
+ {
+ image[i * 4 + 0] = output[i].R;
+ image[i * 4 + 1] = output[i].G;
+ image[i * 4 + 2] = output[i].B;
+ image[i * 4 + 3] = output[i].A;
+ }
+
+ return new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
- else {
+ else
+ {
DXGI_FORMAT format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
- if (file.Header.ddsPixelFormat.IsDxt10Format) {
+ if (file.Header.ddsPixelFormat.IsDxt10Format)
+ {
format = file.Dxt10Header.dxgiFormat;
}
- else {
+ else
+ {
format = file.Header.ddsPixelFormat.DxgiFormat;
}
IBcBlockDecoder decoder = GetDecoder(format, file.Header);
-
+
if (decoder == null)
{
throw new NotSupportedException($"This format is not supported: {format}");
@@ -377,69 +448,112 @@ public Image Decode(DdsFile file)
var blocks = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight, out var blockWidth, out var blockHeight);
- return ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
- (int)pixelWidth, (int)pixelHeight);
+ return new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
+ (int)pixelWidth, (int)pixelHeight)
+ };
}
}
///
/// Read a DdsFile and decode it
///
- public Image[] DecodeAllMipMaps(DdsFile file)
+ public DecodedMipMap[] DecodeAllMipMaps(DdsFile file)
{
- if (IsSupportedRawFormat(file.Header.ddsPixelFormat.DxgiFormat)) {
+ if (IsSupportedRawFormat(file.Header.ddsPixelFormat.DxgiFormat))
+ {
var decoder = GetRawDecoder(file.Header.ddsPixelFormat.DxgiFormat);
- var images = new Image[file.Header.dwMipMapCount];
+ var images = new DecodedMipMap[file.Header.dwMipMapCount];
- for (int mip = 0; mip < file.Header.dwMipMapCount; mip++) {
+ for (int mip = 0; mip < file.Header.dwMipMapCount; mip++)
+ {
var data = file.Faces[0].MipMaps[mip].Data;
var pixelWidth = file.Faces[0].MipMaps[mip].Width;
var pixelHeight = file.Faces[0].MipMaps[mip].Height;
- var image = new Image((int) pixelWidth, (int) pixelHeight);
- var output = decoder.Decode(data, (int) pixelWidth, (int) pixelHeight);
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
+ var image = new byte[(int)pixelWidth * (int)pixelHeight * 4];
+ var output = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight);
- output.CopyTo(pixels);
- images[mip] = image;
+ for (int i = 0; i < output.Length; i++)
+ {
+ image[i * 4 + 0] = output[i].R;
+ image[i * 4 + 1] = output[i].G;
+ image[i * 4 + 2] = output[i].B;
+ image[i * 4 + 3] = output[i].A;
+ }
+ images[mip] = new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
return images;
}
- else {
+ else
+ {
DXGI_FORMAT format = DXGI_FORMAT.DXGI_FORMAT_UNKNOWN;
- if (file.Header.ddsPixelFormat.IsDxt10Format) {
+ if (file.Header.ddsPixelFormat.IsDxt10Format)
+ {
format = file.Dxt10Header.dxgiFormat;
}
- else {
+ else
+ {
format = file.Header.ddsPixelFormat.DxgiFormat;
}
IBcBlockDecoder decoder = GetDecoder(format, file.Header);
-
+
if (decoder == null)
{
throw new NotSupportedException($"This format is not supported: {format}");
}
- var images = new Image[file.Header.dwMipMapCount];
+ var images = new DecodedMipMap[file.Header.dwMipMapCount];
- for (int mip = 0; mip < file.Header.dwMipMapCount; mip++) {
+ for (int mip = 0; mip < file.Header.dwMipMapCount; mip++)
+ {
var data = file.Faces[0].MipMaps[mip].Data;
var pixelWidth = file.Faces[0].MipMaps[mip].Width;
var pixelHeight = file.Faces[0].MipMaps[mip].Height;
- var blocks = decoder.Decode(data, (int) pixelWidth, (int) pixelHeight, out var blockWidth,
+ var blocks = decoder.Decode(data, (int)pixelWidth, (int)pixelHeight, out var blockWidth,
out var blockHeight);
- var image = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
+ var image = ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
(int)pixelWidth, (int)pixelHeight);
- images[mip] = image;
+ images[mip] = new DecodedMipMap()
+ {
+ Width = (int)pixelWidth,
+ Height = (int)pixelHeight,
+ data = image
+ };
}
return images;
}
}
+
+ ///
+ /// Read raw block compressed data and decode it to RGBA.
+ ///
+ public byte[] DecodeRawData(byte[] bcData, int imageWidth, int imageHeight, CompressionFormat format)
+ {
+
+ var decoder = GetDecoder(format);
+ if (decoder == null)
+ {
+ throw new NotSupportedException($"This format is not supported: {format}");
+ }
+
+ var blocks = decoder.Decode(bcData, imageWidth, imageHeight, out var blockWidth, out var blockHeight);
+
+ return ImageToBlocks.ImageFromRawBlocks(blocks, blockWidth, blockHeight,
+ imageWidth, imageHeight);
+
+ }
}
}
diff --git a/BCnEnc.Net/Decoder/RawDecoder.cs b/BCnEnc.Net/Decoder/RawDecoder.cs
index 75f77fe..1a56fca 100644
--- a/BCnEnc.Net/Decoder/RawDecoder.cs
+++ b/BCnEnc.Net/Decoder/RawDecoder.cs
@@ -1,10 +1,10 @@
using System;
-using SixLabors.ImageSharp.PixelFormats;
+using BCnEncoder.Shared;
namespace BCnEncoder.Decoder
{
internal interface IRawDecoder {
- Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight);
+ Rgba32[] Decode(byte[] data, int pixelWidth, int pixelHeight);
}
public class RawRDecoder : IRawDecoder {
@@ -13,7 +13,7 @@ public RawRDecoder(bool redAsLuminance) {
this.redAsLuminance = redAsLuminance;
}
- public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight) {
+ public Rgba32[] Decode(byte[] data, int pixelWidth, int pixelHeight) {
Rgba32[] output = new Rgba32[pixelWidth * pixelHeight];
for (int i = 0; i < output.Length; i++) {
if (redAsLuminance) {
@@ -34,7 +34,7 @@ public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight)
public class RawRGDecoder : IRawDecoder
{
- public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight) {
+ public Rgba32[] Decode(byte[] data, int pixelWidth, int pixelHeight) {
Rgba32[] output = new Rgba32[pixelWidth * pixelHeight];
for (int i = 0; i < output.Length; i++) {
output[i].R = data[i * 2];
@@ -48,7 +48,7 @@ public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight)
public class RawRGBDecoder : IRawDecoder
{
- public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight) {
+ public Rgba32[] Decode(byte[] data, int pixelWidth, int pixelHeight) {
Rgba32[] output = new Rgba32[pixelWidth * pixelHeight];
for (int i = 0; i < output.Length; i++) {
output[i].R = data[i * 3];
@@ -62,7 +62,7 @@ public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight)
public class RawRGBADecoder : IRawDecoder
{
- public Rgba32[] Decode(ReadOnlySpan data, int pixelWidth, int pixelHeight) {
+ public Rgba32[] Decode(byte[] data, int pixelWidth, int pixelHeight) {
Rgba32[] output = new Rgba32[pixelWidth * pixelHeight];
for (int i = 0; i < output.Length; i++) {
output[i].R = data[i * 4];
diff --git a/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs
index 0a0c95d..eb76e9a 100644
--- a/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/Bc1BlockEncoder.cs
@@ -8,24 +8,17 @@ namespace BCnEncoder.Encoder
internal class Bc1BlockEncoder : IBcBlockEncoder
{
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
- if (parallel)
- {
- Parallel.For(0, blocks.Length, i =>
- {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc1Block)];
+ fixed (byte* oDataBytes = outputData)
{
- for (int i = 0; i < blocks.Length; i++)
+ Bc1Block* oDataBlocks = (Bc1Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc1Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
{
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -69,7 +62,7 @@ private static Bc1Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
{
Bc1Block output = new Bc1Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
output.color0 = color0;
output.color1 = color1;
@@ -77,13 +70,13 @@ private static Bc1Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
var c0 = color0.ToColorRgb24();
var c1 = color1.ToColorRgb24();
- ReadOnlySpan colors = output.HasAlphaOrBlack ?
- stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = output.HasAlphaOrBlack ?
+ new ColorRgb24[] {
c0,
c1,
c0 * (1.0 / 2.0) + c1 * (1.0 / 2.0),
new ColorRgb24(0, 0, 0)
- } : stackalloc ColorRgb24[] {
+ } : new ColorRgb24[] {
c0,
c1,
c0 * (2.0 / 3.0) + c1 * (1.0 / 3.0),
@@ -113,7 +106,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
Bc1Block output = new Bc1Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
RgbBoundingBox.Create565(pixels, out var min, out var max);
@@ -132,7 +125,7 @@ private static class Bc1BlockEncoderBalanced {
internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -150,7 +143,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
Bc1Block best = TryColors(rawBlock, c0, c1, out float bestError);
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
if (newC0.data < newC1.data)
{
@@ -185,7 +178,7 @@ private static class Bc1BlockEncoderSlow
internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -205,8 +198,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
int lastChanged = 0;
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
if (newC0.data < newC1.data)
{
var c = newC0;
@@ -242,24 +235,17 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
internal class Bc1AlphaBlockEncoder : IBcBlockEncoder
{
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
- if (parallel)
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc1Block)];
+ fixed (byte* oDataBytes = outputData)
{
- Parallel.For(0, blocks.Length, i =>
- {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else
- {
- for (int i = 0; i < blocks.Length; i++)
+ Bc1Block* oDataBlocks = (Bc1Block*) oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc1Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
{
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -301,7 +287,7 @@ private static Bc1Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
{
Bc1Block output = new Bc1Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
output.color0 = color0;
output.color1 = color1;
@@ -311,13 +297,13 @@ private static Bc1Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
bool hasAlpha = output.HasAlphaOrBlack;
- ReadOnlySpan colors = hasAlpha ?
- stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = hasAlpha ?
+ new ColorRgb24[] {
c0,
c1,
c0 * (1.0 / 2.0) + c1 * (1.0 / 2.0),
new ColorRgb24(0, 0, 0)
- } : stackalloc ColorRgb24[] {
+ } : new ColorRgb24[] {
c0,
c1,
c0 * (2.0 / 3.0) + c1 * (1.0 / 3.0),
@@ -346,7 +332,7 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
Bc1Block output = new Bc1Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
bool hasAlpha = rawBlock.HasTransparentPixels();
@@ -376,7 +362,7 @@ private static class Bc1AlphaBlockEncoderBalanced
internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
bool hasAlpha = rawBlock.HasTransparentPixels();
@@ -400,8 +386,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
Bc1Block best = TryColors(rawBlock, c0, c1, out float bestError);
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
if (!hasAlpha && newC0.data < newC1.data)
{
var c = newC0;
@@ -439,7 +425,7 @@ private static class Bc1AlphaBlockEncoderSlow
internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
bool hasAlpha = rawBlock.HasTransparentPixels();
@@ -464,8 +450,8 @@ internal static Bc1Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
int lastChanged = 0;
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
if (!hasAlpha && newC0.data < newC1.data)
{
var c = newC0;
diff --git a/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs
index 2820452..e84ab77 100644
--- a/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/Bc2BlockEncoder.cs
@@ -8,24 +8,17 @@ namespace BCnEncoder.Encoder
internal class Bc2BlockEncoder : IBcBlockEncoder
{
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
- if (parallel)
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc2Block)];
+ fixed (byte* oDataBytes = outputData)
{
- Parallel.For(0, blocks.Length, i =>
- {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else
- {
- for (int i = 0; i < blocks.Length; i++)
+ Bc2Block* oDataBlocks = (Bc2Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc2Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
{
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -68,7 +61,7 @@ private static Bc2Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
{
Bc2Block output = new Bc2Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
output.color0 = color0;
output.color1 = color1;
@@ -76,7 +69,7 @@ private static Bc2Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
var c0 = color0.ToColorRgb24();
var c1 = color1.ToColorRgb24();
- ReadOnlySpan colors = stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = new ColorRgb24[] {
c0,
c1,
c0 * (2.0 / 3.0) + c1 * (1.0 / 3.0),
@@ -105,7 +98,7 @@ private static class Bc2BlockEncoderFast
internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out var mean, out var principalAxis);
PcaVectors.GetMinMaxColor565(pixels, mean, principalAxis, out var min, out var max);
@@ -125,7 +118,7 @@ private static class Bc2BlockEncoderBalanced {
internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -136,8 +129,8 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
Bc2Block best = TryColors(rawBlock, c0, c1, out float bestError);
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
var block = TryColors(rawBlock, newC0, newC1, out var error);
if (error < bestError)
@@ -165,7 +158,7 @@ private static class Bc2BlockEncoderSlow
internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -185,8 +178,8 @@ internal static Bc2Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
int lastChanged = 0;
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
if (newC0.data < newC1.data)
{
var c = newC0;
diff --git a/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs
index 5957da8..fdc4bc9 100644
--- a/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/Bc3BlockEncoder.cs
@@ -9,24 +9,17 @@ namespace BCnEncoder.Encoder
internal class Bc3BlockEncoder : IBcBlockEncoder
{
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
- if (parallel)
- {
- Parallel.For(0, blocks.Length, i =>
- {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc3Block)];
+ fixed (byte* oDataBytes = outputData)
{
- for (int i = 0; i < blocks.Length; i++)
+ Bc3Block* oDataBlocks = (Bc3Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc3Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
{
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -69,7 +62,7 @@ private static Bc3Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
{
Bc3Block output = new Bc3Block();
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
output.color0 = color0;
output.color1 = color1;
@@ -77,7 +70,7 @@ private static Bc3Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
var c0 = color0.ToColorRgb24();
var c1 = color1.ToColorRgb24();
- ReadOnlySpan colors = stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = new ColorRgb24[] {
c0,
c1,
c0 * (2.0 / 3.0) + c1 * (1.0 / 3.0),
@@ -95,8 +88,54 @@ private static Bc3Block TryColors(RawBlock4X4Rgba32 rawBlock, ColorRgb565 color0
return output;
}
+ private static int SelectAlphaIndices(RawBlock4X4Rgba32 rawBlock, ref Bc3Block block)
+ {
+ int cumulativeError = 0;
+ var a0 = block.Alpha0;
+ var a1 = block.Alpha1;
+ byte[] alphas = a0 > a1 ? new byte[] {
+ a0,
+ a1,
+ (byte)(6 / 7.0 * a0 + 1 / 7.0 * a1),
+ (byte)(5 / 7.0 * a0 + 2 / 7.0 * a1),
+ (byte)(4 / 7.0 * a0 + 3 / 7.0 * a1),
+ (byte)(3 / 7.0 * a0 + 4 / 7.0 * a1),
+ (byte)(2 / 7.0 * a0 + 5 / 7.0 * a1),
+ (byte)(1 / 7.0 * a0 + 6 / 7.0 * a1),
+ } : new byte[] {
+ a0,
+ a1,
+ (byte)(4 / 5.0 * a0 + 1 / 5.0 * a1),
+ (byte)(3 / 5.0 * a0 + 2 / 5.0 * a1),
+ (byte)(2 / 5.0 * a0 + 3 / 5.0 * a1),
+ (byte)(1 / 5.0 * a0 + 4 / 5.0 * a1),
+ 0,
+ 255
+ };
+ var pixels = rawBlock.AsArray;
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ byte bestIndex = 0;
+ int bestError = Math.Abs(pixels[i].A - alphas[0]);
+ for (byte j = 1; j < alphas.Length; j++)
+ {
+ int error = Math.Abs(pixels[i].A - alphas[j]);
+ if (error < bestError)
+ {
+ bestIndex = j;
+ bestError = error;
+ }
+ if (bestError == 0) break;
+ }
+ block.SetAlphaIndex(i, bestIndex);
+ cumulativeError += bestError * bestError;
+ }
+
+ return cumulativeError;
+ }
+
private static Bc3Block FindAlphaValues(Bc3Block colorBlock, RawBlock4X4Rgba32 rawBlock, int variations) {
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
//Find min and max alpha
byte minAlpha = 255;
@@ -113,53 +152,11 @@ private static Bc3Block FindAlphaValues(Bc3Block colorBlock, RawBlock4X4Rgba32 r
}
- int SelectAlphaIndices(ref Bc3Block block) {
- int cumulativeError = 0;
- var a0 = block.Alpha0;
- var a1 = block.Alpha1;
- Span alphas = a0 > a1 ? stackalloc byte[] {
- a0,
- a1,
- (byte) (6/7.0 * a0 + 1/7.0 * a1),
- (byte) (5/7.0 * a0 + 2/7.0 * a1),
- (byte) (4/7.0 * a0 + 3/7.0 * a1),
- (byte) (3/7.0 * a0 + 4/7.0 * a1),
- (byte) (2/7.0 * a0 + 5/7.0 * a1),
- (byte) (1/7.0 * a0 + 6/7.0 * a1),
- } : stackalloc byte[] {
- a0,
- a1,
- (byte) (4/5.0 * a0 + 1/5.0 * a1),
- (byte) (3/5.0 * a0 + 2/5.0 * a1),
- (byte) (2/5.0 * a0 + 3/5.0 * a1),
- (byte) (1/5.0 * a0 + 4/5.0 * a1),
- 0,
- 255
- };
- var pixels = rawBlock.AsSpan;
- for (int i = 0; i < pixels.Length; i++) {
- byte bestIndex = 0;
- int bestError = Math.Abs(pixels[i].A - alphas[0]);
- for (byte j = 1; j < alphas.Length; j++) {
- int error = Math.Abs(pixels[i].A - alphas[j]);
- if (error < bestError) {
- bestIndex = j;
- bestError = error;
- }
- if (bestError == 0) break;
- }
- block.SetAlphaIndex(i, bestIndex);
- cumulativeError += bestError * bestError;
- }
-
- return cumulativeError;
- }
-
//everything is either fully opaque or fully transparent
if (hasExtremeValues && minAlpha == 255 && maxAlpha == 0) {
colorBlock.Alpha0 = 0;
colorBlock.Alpha1 = 255;
- var error = SelectAlphaIndices(ref colorBlock);
+ var error = SelectAlphaIndices(rawBlock, ref colorBlock);
Debug.Assert(0 == error);
return colorBlock;
}
@@ -167,7 +164,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var best = colorBlock;
best.Alpha0 = maxAlpha;
best.Alpha1 = minAlpha;
- int bestError = SelectAlphaIndices(ref best);
+ int bestError = SelectAlphaIndices(rawBlock, ref best);
if (bestError == 0) {
return best;
}
@@ -178,7 +175,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -190,7 +187,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -202,7 +199,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -214,7 +211,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -226,7 +223,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -238,7 +235,7 @@ int SelectAlphaIndices(ref Bc3Block block) {
var block = colorBlock;
block.Alpha0 = hasExtremeValues ? a1 : a0;
block.Alpha1 = hasExtremeValues ? a0 : a1;
- int error = SelectAlphaIndices(ref block);
+ int error = SelectAlphaIndices(rawBlock, ref block);
if (error < bestError) {
best = block;
bestError = error;
@@ -263,7 +260,7 @@ private static class Bc3BlockEncoderFast
internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out var mean, out var principalAxis);
PcaVectors.GetMinMaxColor565(pixels, mean, principalAxis, out var min, out var max);
@@ -291,7 +288,7 @@ private static class Bc3BlockEncoderBalanced {
internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -302,8 +299,8 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
Bc3Block best = TryColors(rawBlock, c0, c1, out float bestError);
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
var block = TryColors(rawBlock, newC0, newC1, out var error);
if (error < bestError)
@@ -331,7 +328,7 @@ private static class Bc3BlockEncoderSlow
internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
{
- var pixels = rawBlock.AsSpan;
+ var pixels = rawBlock.AsArray;
PcaVectors.Create(pixels, out System.Numerics.Vector3 mean, out System.Numerics.Vector3 pa);
PcaVectors.GetMinMaxColor565(pixels, mean, pa, out var min, out var max);
@@ -351,8 +348,8 @@ internal static Bc3Block EncodeBlock(RawBlock4X4Rgba32 rawBlock)
int lastChanged = 0;
for (int i = 0; i < maxTries; i++) {
- var (newC0, newC1) = ColorVariationGenerator.Variate565(c0, c1, i);
-
+ ColorVariationGenerator.Variate565(c0, c1, i, out var newC0, out var newC1);
+
if (newC0.data < newC1.data)
{
var c = newC0;
diff --git a/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs
index 456907d..740a665 100644
--- a/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/Bc4BlockEncoder.cs
@@ -13,20 +13,17 @@ public Bc4BlockEncoder(bool luminanceAsRed) {
this.luminanceAsRed = luminanceAsRed;
}
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality,
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality,
bool parallel) {
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
- if (parallel) {
- Parallel.For(0, blocks.Length, i => {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else {
- for (int i = 0; i < blocks.Length; i++) {
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc4Block)];
+ fixed (byte* oDataBytes = outputData)
+ {
+ Bc4Block* oDataBlocks = (Bc4Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc4Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
+ {
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -36,7 +33,7 @@ public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight
private Bc4Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality) {
Bc4Block output = new Bc4Block();
byte[] colors = new byte[16];
- var pixels = block.AsSpan;
+ var pixels = block.AsArray;
for (int i = 0; i < 16; i++) {
if (luminanceAsRed) {
colors[i] = (byte)(new ColorYCbCr(pixels[i]).y * 255);
@@ -71,6 +68,55 @@ public DXGI_FORMAT GetDxgiFormat() {
}
#region Encoding private stuff
+
+ private static int SelectIndices(ref Bc4Block block, byte[] pixels)
+ {
+ int cumulativeError = 0;
+ var c0 = block.Red0;
+ var c1 = block.Red1;
+ byte[] colors = c0 > c1
+ ? new byte[] {
+ c0,
+ c1,
+ (byte) (6 / 7.0 * c0 + 1 / 7.0 * c1),
+ (byte) (5 / 7.0 * c0 + 2 / 7.0 * c1),
+ (byte) (4 / 7.0 * c0 + 3 / 7.0 * c1),
+ (byte) (3 / 7.0 * c0 + 4 / 7.0 * c1),
+ (byte) (2 / 7.0 * c0 + 5 / 7.0 * c1),
+ (byte) (1 / 7.0 * c0 + 6 / 7.0 * c1),
+ }
+ : new byte[] {
+ c0,
+ c1,
+ (byte) (4 / 5.0 * c0 + 1 / 5.0 * c1),
+ (byte) (3 / 5.0 * c0 + 2 / 5.0 * c1),
+ (byte) (2 / 5.0 * c0 + 3 / 5.0 * c1),
+ (byte) (1 / 5.0 * c0 + 4 / 5.0 * c1),
+ 0,
+ 255
+ };
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ byte bestIndex = 0;
+ int bestError = Math.Abs(pixels[i] - colors[0]);
+ for (byte j = 1; j < colors.Length; j++)
+ {
+ int error = Math.Abs(pixels[i] - colors[j]);
+ if (error < bestError)
+ {
+ bestIndex = j;
+ bestError = error;
+ }
+
+ if (bestError == 0) break;
+ }
+
+ block.SetRedIndex(i, bestIndex);
+ cumulativeError += bestError * bestError;
+ }
+
+ return cumulativeError;
+ }
private static Bc4Block FindRedValues(Bc4Block colorBlock, byte[] pixels, int variations) {
@@ -88,57 +134,11 @@ private static Bc4Block FindRedValues(Bc4Block colorBlock, byte[] pixels, int va
}
}
-
- int SelectIndices(ref Bc4Block block) {
- int cumulativeError = 0;
- var c0 = block.Red0;
- var c1 = block.Red1;
- Span colors = c0 > c1
- ? stackalloc byte[] {
- c0,
- c1,
- (byte) (6 / 7.0 * c0 + 1 / 7.0 * c1),
- (byte) (5 / 7.0 * c0 + 2 / 7.0 * c1),
- (byte) (4 / 7.0 * c0 + 3 / 7.0 * c1),
- (byte) (3 / 7.0 * c0 + 4 / 7.0 * c1),
- (byte) (2 / 7.0 * c0 + 5 / 7.0 * c1),
- (byte) (1 / 7.0 * c0 + 6 / 7.0 * c1),
- }
- : stackalloc byte[] {
- c0,
- c1,
- (byte) (4 / 5.0 * c0 + 1 / 5.0 * c1),
- (byte) (3 / 5.0 * c0 + 2 / 5.0 * c1),
- (byte) (2 / 5.0 * c0 + 3 / 5.0 * c1),
- (byte) (1 / 5.0 * c0 + 4 / 5.0 * c1),
- 0,
- 255
- };
- for (int i = 0; i < pixels.Length; i++) {
- byte bestIndex = 0;
- int bestError = Math.Abs(pixels[i] - colors[0]);
- for (byte j = 1; j < colors.Length; j++) {
- int error = Math.Abs(pixels[i] - colors[j]);
- if (error < bestError) {
- bestIndex = j;
- bestError = error;
- }
-
- if (bestError == 0) break;
- }
-
- block.SetRedIndex(i, bestIndex);
- cumulativeError += bestError * bestError;
- }
-
- return cumulativeError;
- }
-
//everything is either fully black or fully red
if (hasExtremeValues && min == 255 && max == 0) {
colorBlock.Red0 = 0;
colorBlock.Red1 = 255;
- var error = SelectIndices(ref colorBlock);
+ var error = SelectIndices(ref colorBlock, pixels);
Debug.Assert(0 == error);
return colorBlock;
}
@@ -146,7 +146,7 @@ int SelectIndices(ref Bc4Block block) {
var best = colorBlock;
best.Red0 = max;
best.Red1 = min;
- int bestError = SelectIndices(ref best);
+ int bestError = SelectIndices(ref best, pixels);
if (bestError == 0) {
return best;
}
@@ -158,7 +158,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
@@ -172,7 +172,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
@@ -186,7 +186,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
@@ -200,7 +200,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
@@ -214,7 +214,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
@@ -228,7 +228,7 @@ int SelectIndices(ref Bc4Block block) {
var block = colorBlock;
block.Red0 = hasExtremeValues ? c1 : c0;
block.Red1 = hasExtremeValues ? c0 : c1;
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels);
if (error < bestError) {
best = block;
bestError = error;
diff --git a/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs b/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs
index de1c28a..e2d25d7 100644
--- a/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/Bc5BlockEncoder.cs
@@ -8,20 +8,17 @@ namespace BCnEncoder.Encoder
{
internal class Bc5BlockEncoder : IBcBlockEncoder {
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc5Block)];
+ fixed (byte* oDataBytes = outputData)
+ {
+ Bc5Block* oDataBlocks = (Bc5Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc5Block);
- if (parallel) {
- Parallel.For(0, blocks.Length, i => {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else {
- for (int i = 0; i < blocks.Length; i++) {
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ for (int i = 0; i < oDataBlocksLength; i++)
+ {
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
@@ -32,7 +29,7 @@ private Bc5Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality
Bc5Block output = new Bc5Block();
byte[] reds = new byte[16];
byte[] greens = new byte[16];
- var pixels = block.AsSpan;
+ var pixels = block.AsArray;
for (int i = 0; i < 16; i++) {
reds[i] = pixels[i].R;
greens[i] = pixels[i].G;
@@ -59,43 +56,43 @@ private Bc5Block EncodeBlock(RawBlock4X4Rgba32 block, CompressionQuality quality
output = FindValues(output, reds, variations, errorThreshold,
- (block, i, idx) => {
- block.SetRedIndex(i, idx);
- return block;
+ (blck, i, idx) => {
+ blck.SetRedIndex(i, idx);
+ return blck;
},
- (block, col) => {
- block.Red0 = col;
- return block;
+ (blck, col) => {
+ blck.Red0 = col;
+ return blck;
},
- (block, col) => {
- block.Red1 = col;
- return block;
+ (blck, col) => {
+ blck.Red1 = col;
+ return blck;
},
- (block) => {
- return block.Red0;
+ (blck) => {
+ return blck.Red0;
},
- (block) => {
- return block.Red1;
+ (blck) => {
+ return blck.Red1;
}
);
output = FindValues(output, greens, variations, errorThreshold,
- (block, i, idx) => {
- block.SetGreenIndex(i, idx);
- return block;
+ (blck, i, idx) => {
+ blck.SetGreenIndex(i, idx);
+ return blck;
},
- (block, col) => {
- block.Green0 = col;
- return block;
+ (blck, col) => {
+ blck.Green0 = col;
+ return blck;
},
- (block, col) => {
- block.Green1 = col;
- return block;
+ (blck, col) => {
+ blck.Green1 = col;
+ return blck;
},
- (block) => {
- return block.Green0;
+ (blck) => {
+ return blck.Green0;
},
- (block) => {
- return block.Green1;
+ (blck) => {
+ return blck.Green1;
});
return output;
}
@@ -114,6 +111,61 @@ public DXGI_FORMAT GetDxgiFormat() {
#region Encoding private stuff
+ private static int SelectIndices(ref Bc5Block block, byte[] pixels,
+ Func indexSetter,
+ Func col0Getter,
+ Func col1Getter)
+ {
+ int cumulativeError = 0;
+ //var c0 = block.Red0;
+ //var c1 = block.Red1;
+ var c0 = col0Getter(block);
+ var c1 = col1Getter(block);
+ byte[] colors = c0 > c1
+ ? new byte[] {
+ c0,
+ c1,
+ (byte) (6 / 7.0 * c0 + 1 / 7.0 * c1),
+ (byte) (5 / 7.0 * c0 + 2 / 7.0 * c1),
+ (byte) (4 / 7.0 * c0 + 3 / 7.0 * c1),
+ (byte) (3 / 7.0 * c0 + 4 / 7.0 * c1),
+ (byte) (2 / 7.0 * c0 + 5 / 7.0 * c1),
+ (byte) (1 / 7.0 * c0 + 6 / 7.0 * c1),
+ }
+ : new byte[] {
+ c0,
+ c1,
+ (byte) (4 / 5.0 * c0 + 1 / 5.0 * c1),
+ (byte) (3 / 5.0 * c0 + 2 / 5.0 * c1),
+ (byte) (2 / 5.0 * c0 + 3 / 5.0 * c1),
+ (byte) (1 / 5.0 * c0 + 4 / 5.0 * c1),
+ 0,
+ 255
+ };
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ byte bestIndex = 0;
+ int bestError = Math.Abs(pixels[i] - colors[0]);
+ for (byte j = 1; j < colors.Length; j++)
+ {
+ int error = Math.Abs(pixels[i] - colors[j]);
+ if (error < bestError)
+ {
+ bestIndex = j;
+ bestError = error;
+ }
+
+ if (bestError == 0) break;
+ }
+
+ block = indexSetter(block, i, bestIndex);
+ //block.SetRedIndex(i, bestIndex);
+ cumulativeError += bestError * bestError;
+ }
+
+ return cumulativeError;
+ }
+
private static Bc5Block FindValues(Bc5Block colorBlock, byte[] pixels, int variations, int errorThreshold,
Func indexSetter,
Func col0Setter,
@@ -135,62 +187,13 @@ private static Bc5Block FindValues(Bc5Block colorBlock, byte[] pixels, int varia
}
}
-
- int SelectIndices(ref Bc5Block block) {
- int cumulativeError = 0;
- //var c0 = block.Red0;
- //var c1 = block.Red1;
- var c0 = col0Getter(block);
- var c1 = col1Getter(block);
- Span colors = c0 > c1
- ? stackalloc byte[] {
- c0,
- c1,
- (byte) (6 / 7.0 * c0 + 1 / 7.0 * c1),
- (byte) (5 / 7.0 * c0 + 2 / 7.0 * c1),
- (byte) (4 / 7.0 * c0 + 3 / 7.0 * c1),
- (byte) (3 / 7.0 * c0 + 4 / 7.0 * c1),
- (byte) (2 / 7.0 * c0 + 5 / 7.0 * c1),
- (byte) (1 / 7.0 * c0 + 6 / 7.0 * c1),
- }
- : stackalloc byte[] {
- c0,
- c1,
- (byte) (4 / 5.0 * c0 + 1 / 5.0 * c1),
- (byte) (3 / 5.0 * c0 + 2 / 5.0 * c1),
- (byte) (2 / 5.0 * c0 + 3 / 5.0 * c1),
- (byte) (1 / 5.0 * c0 + 4 / 5.0 * c1),
- 0,
- 255
- };
- for (int i = 0; i < pixels.Length; i++) {
- byte bestIndex = 0;
- int bestError = Math.Abs(pixels[i] - colors[0]);
- for (byte j = 1; j < colors.Length; j++) {
- int error = Math.Abs(pixels[i] - colors[j]);
- if (error < bestError) {
- bestIndex = j;
- bestError = error;
- }
-
- if (bestError == 0) break;
- }
-
- block = indexSetter(block, i, bestIndex);
- //block.SetRedIndex(i, bestIndex);
- cumulativeError += bestError * bestError;
- }
-
- return cumulativeError;
- }
-
//everything is either fully black or fully red
if (hasExtremeValues && min == 255 && max == 0) {
//colorBlock.Red0 = 0;
//colorBlock.Red1 = 255;
colorBlock = col0Setter(colorBlock, 0);
colorBlock = col1Setter(colorBlock, 255);
- var error = SelectIndices(ref colorBlock);
+ var error = SelectIndices(ref colorBlock, pixels, indexSetter, col0Getter, col1Getter);
Debug.Assert(0 == error);
return colorBlock;
}
@@ -200,7 +203,7 @@ int SelectIndices(ref Bc5Block block) {
//best.Red1 = min;
best = col0Setter(best, max);
best = col1Setter(best, min);
- int bestError = SelectIndices(ref best);
+ int bestError = SelectIndices(ref best, pixels, indexSetter, col0Getter, col1Getter);
if (bestError == 0) {
return best;
}
@@ -214,7 +217,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
@@ -230,7 +233,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
@@ -246,7 +249,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
@@ -262,7 +265,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
@@ -278,7 +281,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
@@ -294,7 +297,7 @@ int SelectIndices(ref Bc5Block block) {
//block.Red1 = hasExtremeValues ? c0 : c1;
block = col0Setter(block, hasExtremeValues ? c1 : c0);
block = col1Setter(block, hasExtremeValues ? c0 : c1);
- int error = SelectIndices(ref block);
+ int error = SelectIndices(ref block, pixels, indexSetter, col0Getter, col1Getter);
if (error < bestError) {
best = block;
bestError = error;
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs
index cde1c20..e78ea92 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Encoder.cs
@@ -9,29 +9,20 @@ namespace BCnEncoder.Encoder.Bc7
internal class Bc7Encoder : IBcBlockEncoder
{
- public byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
+ public unsafe byte[] Encode(RawBlock4X4Rgba32[] blocks, int blockWidth, int blockHeight, CompressionQuality quality, bool parallel)
{
- byte[] outputData = new byte[blockWidth * blockHeight * Marshal.SizeOf()];
- Span outputBlocks = MemoryMarshal.Cast(outputData);
-
-
- if (parallel)
+ byte[] outputData = new byte[blockWidth * blockHeight * sizeof(Bc7Block)];
+ fixed (byte* oDataBytes = outputData)
{
- Parallel.For(0, blocks.Length, i =>
- {
- Span outputBlocks = MemoryMarshal.Cast(outputData);
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
- });
- }
- else
- {
- for (int i = 0; i < blocks.Length; i++)
+ Bc7Block* oDataBlocks = (Bc7Block*)oDataBytes;
+ int oDataBlocksLength = outputData.Length / sizeof(Bc7Block);
+
+ for (int i = 0; i < oDataBlocksLength; i++)
{
- outputBlocks[i] = EncodeBlock(blocks[i], quality);
+ oDataBlocks[i] = EncodeBlock(blocks[i], quality);
}
}
-
return outputData;
}
@@ -55,10 +46,10 @@ private static ClusterIndices4X4 CreateClusterIndexBlock(RawBlock4X4Rgba32 raw,
ClusterIndices4X4 indexBlock = new ClusterIndices4X4();
- var indices = LinearClustering.ClusterPixels(raw.AsSpan, 4, 4,
+ var indices = LinearClustering.ClusterPixels(raw.AsArray, 4, 4,
numClusters, 1, 10, false);
- var output = indexBlock.AsSpan;
+ var output = indexBlock.AsArray;
for (int i = 0; i < output.Length; i++)
{
output[i] = indices[i];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7EncodingHelpers.cs b/BCnEnc.Net/Encoder/Bc7/Bc7EncodingHelpers.cs
index 24371cd..ff55fc8 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7EncodingHelpers.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7EncodingHelpers.cs
@@ -2,7 +2,6 @@
using System.Linq;
using System.Runtime.InteropServices;
using BCnEncoder.Shared;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Encoder.Bc7
{
@@ -13,26 +12,115 @@ internal struct ClusterIndices4X4
public int i02, i12, i22, i32;
public int i03, i13, i23, i33;
- public Span AsSpan => MemoryMarshal.CreateSpan(ref i00, 16);
+ public int[] AsArray => new int[]
+ {
+ i00, i10, i20, i30,
+ i01, i11, i21, i31,
+ i02, i12, i22, i32,
+ i03, i13, i23, i33
+ };
public int this[int x, int y]
{
- get => AsSpan[x + y * 4];
- set => AsSpan[x + y * 4] = value;
+ get
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: return i00;
+ case 1: return i01;
+ case 2: return i02;
+ case 3: return i03;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: return i10;
+ case 1: return i11;
+ case 2: return i12;
+ case 3: return i13;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: return i20;
+ case 1: return i21;
+ case 2: return i22;
+ case 3: return i23;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: return i30;
+ case 1: return i31;
+ case 2: return i32;
+ case 3: return i33;
+ }
+ break;
+ }
+ return default;
+ }
+ set
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: i00 = value; break;
+ case 1: i01 = value; break;
+ case 2: i02 = value; break;
+ case 3: i03 = value; break;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: i10 = value; break;
+ case 1: i11 = value; break;
+ case 2: i12 = value; break;
+ case 3: i13 = value; break;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: i20 = value; break;
+ case 1: i21 = value; break;
+ case 2: i22 = value; break;
+ case 3: i23 = value; break;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: i30 = value; break;
+ case 1: i31 = value; break;
+ case 2: i32 = value; break;
+ case 3: i33 = value; break;
+ }
+ break;
+ }
+ }
}
public int this[int index]
{
- get => AsSpan[index];
- set => AsSpan[index] = value;
+ get => this[index % 4, (int)Math.Floor(index / 4.0)];
+ set => this[index % 4, (int)Math.Floor(index / 4.0)] = value;
}
-
+
public int NumClusters
{
get
{
- var t = AsSpan;
- Span clusters = stackalloc int[16];
+ var t = AsArray;
+ int[] clusters = new int[16];
int distinct = 0;
for (int i = 0; i < 16; i++)
{
@@ -64,9 +152,9 @@ public ClusterIndices4X4 Reduce(out int numClusters)
{
var result = new ClusterIndices4X4();
numClusters = NumClusters;
- Span mapKey = stackalloc int[numClusters];
- var indices = AsSpan;
- var outIndices = result.AsSpan;
+ int[] mapKey = new int[numClusters];
+ var indices = AsArray;
+ var outIndices = result.AsArray;
int next = 0;
for (int i = 0; i < 16; i++)
{
@@ -97,113 +185,139 @@ internal static class Bc7EncodingHelpers
{
private static int[] varPatternRAlpha = new int[] { 1, -1, 1, 0, 0, -1, 0, 0, 0, 0 };
private static int[] varPatternRNoAlpha = new int[] { 1, -1, 1, 0, 0, -1, 0, 0 };
-
+
private static int[] varPatternGAlpha = new int[] { 1, -1, 0, 1, 0, 0, -1, 0, 0, 0 };
private static int[] varPatternGNoAlpha = new int[] { 1, -1, 0, 1, 0, 0, -1, 0 };
-
+
private static int[] varPatternBAlpha = new int[] { 1, -1, 0, 0, 1, 0, 0, -1, 0, 0 };
private static int[] varPatternBNoAlpha = new int[] { 1, -1, 0, 0, 1, 0, 0, -1 };
-
+
private static int[] varPatternAAlpha = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 1, -1 };
private static int[] varPatternANoAlpha = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
- public static bool TypeHasPBits(Bc7BlockType type) => type switch
+ public static bool TypeHasPBits(Bc7BlockType type)
{
- Bc7BlockType.Type0 => true,
- Bc7BlockType.Type1 => true,
- Bc7BlockType.Type3 => true,
- Bc7BlockType.Type6 => true,
- Bc7BlockType.Type7 => true,
- _ => false
- };
+ switch (type)
+ {
+ case (Bc7BlockType.Type0): return true;
+ case (Bc7BlockType.Type1): return true;
+ case (Bc7BlockType.Type3): return true;
+ case (Bc7BlockType.Type6): return true;
+ case (Bc7BlockType.Type7): return true;
+ default: return false;
+ }
+ }
- public static bool TypeHasSharedPBits(Bc7BlockType type) => type switch
+ public static bool TypeHasSharedPBits(Bc7BlockType type)
{
- Bc7BlockType.Type1 => true,
- _ => false
- };
+ return type == Bc7BlockType.Type1;
+ }
///
/// Includes PBit
///
- public static int GetColorComponentPrecisionWithPBit(Bc7BlockType type) => type switch
+ public static int GetColorComponentPrecisionWithPBit(Bc7BlockType type)
{
- Bc7BlockType.Type0 => 5,
- Bc7BlockType.Type1 => 7,
- Bc7BlockType.Type2 => 5,
- Bc7BlockType.Type3 => 8,
- Bc7BlockType.Type4 => 5,
- Bc7BlockType.Type5 => 7,
- Bc7BlockType.Type6 => 8,
- Bc7BlockType.Type7 => 6,
- _ => 0
- };
+ switch (type)
+ {
+ case (Bc7BlockType.Type0): return 5;
+ case (Bc7BlockType.Type1): return 7;
+ case (Bc7BlockType.Type2): return 5;
+ case (Bc7BlockType.Type3): return 8;
+ case (Bc7BlockType.Type4): return 5;
+ case (Bc7BlockType.Type5): return 7;
+ case (Bc7BlockType.Type6): return 8;
+ case (Bc7BlockType.Type7): return 6;
+ default: return 0;
+ }
+ }
///
/// Includes PBit
///
- public static int GetAlphaComponentPrecisionWithPBit(Bc7BlockType type) => type switch
+ public static int GetAlphaComponentPrecisionWithPBit(Bc7BlockType type)
{
-
- Bc7BlockType.Type4 => 6,
- Bc7BlockType.Type5 => 8,
- Bc7BlockType.Type6 => 8,
- Bc7BlockType.Type7 => 6,
- _ => 0
- };
+ switch (type)
+ {
+ case
+ Bc7BlockType.Type4:
+ return 6;
+ case
+ Bc7BlockType.Type5:
+ return 8;
+ case
+ Bc7BlockType.Type6:
+ return 8;
+ case
+ Bc7BlockType.Type7:
+ return 6;
+ default: return 0;
+ }
+ }
///
/// Does not include pBit
///
- public static int GetColorComponentPrecision(Bc7BlockType type) => type switch
+ public static int GetColorComponentPrecision(Bc7BlockType type)
{
- Bc7BlockType.Type0 => 4,
- Bc7BlockType.Type1 => 6,
- Bc7BlockType.Type2 => 5,
- Bc7BlockType.Type3 => 7,
- Bc7BlockType.Type4 => 5,
- Bc7BlockType.Type5 => 7,
- Bc7BlockType.Type6 => 7,
- Bc7BlockType.Type7 => 5,
- _ => 0
- };
+ switch (type)
+ {
+ case Bc7BlockType.Type0: return 4;
+ case Bc7BlockType.Type1: return 6;
+ case Bc7BlockType.Type2: return 5;
+ case Bc7BlockType.Type3: return 7;
+ case Bc7BlockType.Type4: return 5;
+ case Bc7BlockType.Type5: return 7;
+ case Bc7BlockType.Type6: return 7;
+ case Bc7BlockType.Type7: return 5;
+ default: return 0;
+ }
+ }
///
/// Does not include pBit
///
- public static int GetAlphaComponentPrecision(Bc7BlockType type) => type switch
+ public static int GetAlphaComponentPrecision(Bc7BlockType type)
{
+ switch (type)
+ {
+ case Bc7BlockType.Type4: return 6;
+ case Bc7BlockType.Type5: return 8;
+ case Bc7BlockType.Type6: return 7;
+ case Bc7BlockType.Type7: return 5;
+ default: return 0;
+ }
+ }
- Bc7BlockType.Type4 => 6,
- Bc7BlockType.Type5 => 8,
- Bc7BlockType.Type6 => 7,
- Bc7BlockType.Type7 => 5,
- _ => 0
- };
-
- public static int GetColorIndexBitCount(Bc7BlockType type, int type4IdxMode = 0) => type switch
+ public static int GetColorIndexBitCount(Bc7BlockType type, int type4IdxMode = 0)
{
- Bc7BlockType.Type0 => 3,
- Bc7BlockType.Type1 => 3,
- Bc7BlockType.Type2 => 2,
- Bc7BlockType.Type3 => 2,
- Bc7BlockType.Type4 when type4IdxMode == 0 => 2,
- Bc7BlockType.Type4 when type4IdxMode == 1 => 3,
- Bc7BlockType.Type5 => 2,
- Bc7BlockType.Type6 => 4,
- Bc7BlockType.Type7 => 2,
- _ => 0
- };
- public static int GetAlphaIndexBitCount(Bc7BlockType type, int type4IdxMode = 0) => type switch
+ switch (type)
+ {
+ case Bc7BlockType.Type0: return 3;
+ case Bc7BlockType.Type1: return 3;
+ case Bc7BlockType.Type2: return 2;
+ case Bc7BlockType.Type3: return 2;
+ case Bc7BlockType.Type4: return type4IdxMode == 0 ? 2 : 3;
+ case Bc7BlockType.Type5: return 2;
+ case Bc7BlockType.Type6: return 4;
+ case Bc7BlockType.Type7: return 2;
+ default: return 0;
+ }
+
+ }
+
+ public static int GetAlphaIndexBitCount(Bc7BlockType type, int type4IdxMode = 0)
{
- Bc7BlockType.Type4 when type4IdxMode == 0 => 3,
- Bc7BlockType.Type4 when type4IdxMode == 1 => 2,
- Bc7BlockType.Type5 => 2,
- Bc7BlockType.Type6 => 4,
- Bc7BlockType.Type7 => 2,
- _ => 0
- };
+ switch (type)
+ {
+ case Bc7BlockType.Type4: return type4IdxMode == 0 ? 3 : 2;
+ case Bc7BlockType.Type5: return 2;
+ case Bc7BlockType.Type6: return 4;
+ case Bc7BlockType.Type7: return 2;
+ default: return 0;
+ }
+ }
public static void ExpandEndpoints(Bc7BlockType type, ColorRgba32[] endpoints, byte[] pBits)
@@ -302,9 +416,9 @@ public static int SelectBest2SubsetPartition(ClusterIndices4X4 reducedIndicesBlo
int CalculatePartitionError(int partitionIndex)
{
int error = 0;
- ReadOnlySpan partitionTable = Bc7Block.Subsets2PartitionTable[partitionIndex];
- Span subset0 = stackalloc int[numDistinctClusters];
- Span subset1 = stackalloc int[numDistinctClusters];
+ int[] partitionTable = Bc7Block.Subsets2PartitionTable[partitionIndex];
+ int[] subset0 = new int[numDistinctClusters];
+ int[] subset1 = new int[numDistinctClusters];
int max0Idx = 0;
int max1Idx = 0;
@@ -377,13 +491,13 @@ public static int[] Rank2SubsetPartitions(ClusterIndices4X4 reducedIndicesBlock,
{
int[] output = Enumerable.Range(0, 64).ToArray();
-
+
int CalculatePartitionError(int partitionIndex)
{
int error = 0;
- ReadOnlySpan partitionTable = Bc7Block.Subsets2PartitionTable[partitionIndex];
- Span subset0 = stackalloc int[numDistinctClusters];
- Span subset1 = stackalloc int[numDistinctClusters];
+ int[] partitionTable = Bc7Block.Subsets2PartitionTable[partitionIndex];
+ int[] subset0 = new int[numDistinctClusters];
+ int[] subset1 = new int[numDistinctClusters];
int max0Idx = 0;
int max1Idx = 0;
@@ -444,11 +558,11 @@ public static int SelectBest3SubsetPartition(ClusterIndices4X4 reducedIndicesBlo
int CalculatePartitionError(int partitionIndex)
{
int error = 0;
- ReadOnlySpan partitionTable = Bc7Block.Subsets3PartitionTable[partitionIndex];
+ int[] partitionTable = Bc7Block.Subsets3PartitionTable[partitionIndex];
- Span subset0 = stackalloc int[numDistinctClusters];
- Span subset1 = stackalloc int[numDistinctClusters];
- Span subset2 = stackalloc int[numDistinctClusters];
+ int[] subset0 = new int[numDistinctClusters];
+ int[] subset1 = new int[numDistinctClusters];
+ int[] subset2 = new int[numDistinctClusters];
int max0Idx = 0;
int max1Idx = 0;
int max2Idx = 0;
@@ -539,11 +653,11 @@ public static int[] Rank3SubsetPartitions(ClusterIndices4X4 reducedIndicesBlock,
int CalculatePartitionError(int partitionIndex)
{
int error = 0;
- ReadOnlySpan partitionTable = Bc7Block.Subsets3PartitionTable[partitionIndex];
+ int[] partitionTable = Bc7Block.Subsets3PartitionTable[partitionIndex];
- Span subset0 = stackalloc int[numDistinctClusters];
- Span subset1 = stackalloc int[numDistinctClusters];
- Span subset2 = stackalloc int[numDistinctClusters];
+ int[] subset0 = new int[numDistinctClusters];
+ int[] subset1 = new int[numDistinctClusters];
+ int[] subset2 = new int[numDistinctClusters];
int max0Idx = 0;
int max1Idx = 0;
int max2Idx = 0;
@@ -613,19 +727,19 @@ public static void GetInitialUnscaledEndpoints(RawBlock4X4Rgba32 block, out Colo
out ColorRgba32 ep1)
{
- var originalPixels = block.AsSpan;
+ var originalPixels = block.AsArray;
PcaVectors.CreateWithAlpha(originalPixels, out var mean, out var pa);
- PcaVectors.GetExtremePointsWithAlpha(block.AsSpan, mean, pa, out var min, out var max);
+ PcaVectors.GetExtremePointsWithAlpha(block.AsArray, mean, pa, out var min, out var max);
ep0 = new ColorRgba32((byte)(min.X * 255), (byte)(min.Y * 255), (byte)(min.Z * 255), (byte)(min.W * 255));
ep1 = new ColorRgba32((byte)(max.X * 255), (byte)(max.Y * 255), (byte)(max.Z * 255), (byte)(max.W * 255));
}
public static void GetInitialUnscaledEndpointsForSubset(RawBlock4X4Rgba32 block, out ColorRgba32 ep0,
- out ColorRgba32 ep1, ReadOnlySpan partitionTable, int subsetIndex)
+ out ColorRgba32 ep1, int[] partitionTable, int subsetIndex)
{
- var originalPixels = block.AsSpan;
+ var originalPixels = block.AsArray;
int count = 0;
for (int i = 0; i < 16; i++)
@@ -636,7 +750,7 @@ public static void GetInitialUnscaledEndpointsForSubset(RawBlock4X4Rgba32 block,
}
}
- Span subsetColors = stackalloc Rgba32[count];
+ Rgba32[] subsetColors = new Rgba32[count];
int next = 0;
for (int i = 0; i < 16; i++)
{
@@ -647,7 +761,7 @@ public static void GetInitialUnscaledEndpointsForSubset(RawBlock4X4Rgba32 block,
}
PcaVectors.CreateWithAlpha(subsetColors, out var mean, out var pa);
- PcaVectors.GetExtremePointsWithAlpha(block.AsSpan, mean, pa, out var min, out var max);
+ PcaVectors.GetExtremePointsWithAlpha(block.AsArray, mean, pa, out var min, out var max);
ep0 = new ColorRgba32((byte)(min.X * 255), (byte)(min.Y * 255), (byte)(min.Z * 255), (byte)(min.W * 255));
ep1 = new ColorRgba32((byte)(max.X * 255), (byte)(max.Y * 255), (byte)(max.Z * 255), (byte)(max.W * 255));
@@ -708,16 +822,16 @@ public static ColorRgba32 InterpolateColor(ColorRgba32 endPointStart, ColorRgba3
byte InterpolateByte(byte e0, byte e1, int index, int indexPrecision)
{
if (indexPrecision == 0) return e0;
- ReadOnlySpan aWeights2 = Bc7Block.colorInterpolationWeights2;
- ReadOnlySpan aWeights3 = Bc7Block.colorInterpolationWeights3;
- ReadOnlySpan aWeights4 = Bc7Block.colorInterpolationWeights4;
-
- if(indexPrecision == 2)
- return (byte) (((64 - aWeights2[index])* (e0) + aWeights2[index]*(e1) + 32) >> 6);
- else if(indexPrecision == 3)
- return (byte) (((64 - aWeights3[index])*(e0) + aWeights3[index]*(e1) + 32) >> 6);
+ byte[] aWeights2 = Bc7Block.colorInterpolationWeights2;
+ byte[] aWeights3 = Bc7Block.colorInterpolationWeights3;
+ byte[] aWeights4 = Bc7Block.colorInterpolationWeights4;
+
+ if (indexPrecision == 2)
+ return (byte)(((64 - aWeights2[index]) * (e0) + aWeights2[index] * (e1) + 32) >> 6);
+ else if (indexPrecision == 3)
+ return (byte)(((64 - aWeights3[index]) * (e0) + aWeights3[index] * (e1) + 32) >> 6);
else // indexprecision == 4
- return (byte) (((64 - aWeights4[index])*(e0) + aWeights4[index]*(e1) + 32) >> 6);
+ return (byte)(((64 - aWeights4[index]) * (e0) + aWeights4[index] * (e1) + 32) >> 6);
}
ColorRgba32 result = new ColorRgba32(
@@ -738,7 +852,7 @@ public static void ClampEndpoint(ref ColorRgba32 endpoint, byte colorMax, byte a
if (endpoint.a > alphaMax) endpoint.a = alphaMax;
}
- private static int FindClosestColorIndex(ColorYCbCrAlpha color, ReadOnlySpan colors, out float bestError)
+ private static int FindClosestColorIndex(ColorYCbCrAlpha color, ColorYCbCrAlpha[] colors, out float bestError)
{
bestError = color.CalcDistWeighted(colors[0], 4, 2);
int bestIndex = 0;
@@ -754,7 +868,7 @@ private static int FindClosestColorIndex(ColorYCbCrAlpha color, ReadOnlySpan colors, out float bestError)
+ private static int FindClosestColorIndex(ColorYCbCr color, ColorYCbCr[] colors, out float bestError)
{
bestError = color.CalcDistWeighted(colors[0], 4);
int bestIndex = 0;
@@ -774,7 +888,7 @@ private static int FindClosestColorIndex(ColorYCbCr color, ReadOnlySpan alphas, out float bestError)
+ private static int FindClosestAlphaIndex(byte alpha, byte[] alphas, out float bestError)
{
bestError = (alpha - alphas[0]) * (alpha - alphas[0]);
int bestIndex = 0;
@@ -797,15 +911,15 @@ private static int FindClosestAlphaIndex(byte alpha, ReadOnlySpan alphas,
private static float TrySubsetEndpoints(Bc7BlockType type, RawBlock4X4Rgba32 raw, ColorRgba32 ep0, ColorRgba32 ep1,
- ReadOnlySpan partitionTable, int subsetIndex, int type4IdxMode)
+ int[] partitionTable, int subsetIndex, int type4IdxMode)
{
int colorIndexPrecision = GetColorIndexBitCount(type, type4IdxMode);
int alphaIndexPrecision = GetAlphaIndexBitCount(type, type4IdxMode);
if (type == Bc7BlockType.Type4 || type == Bc7BlockType.Type5)
{ //separate indices for color and alpha
- Span colors = stackalloc ColorYCbCr[1 << colorIndexPrecision];
- Span alphas = stackalloc byte[1 << alphaIndexPrecision];
+ ColorYCbCr[] colors = new ColorYCbCr[1 << colorIndexPrecision];
+ byte[] alphas = new byte[1 << alphaIndexPrecision];
for (int i = 0; i < colors.Length; i++)
{
@@ -819,7 +933,7 @@ private static float TrySubsetEndpoints(Bc7BlockType type, RawBlock4X4Rgba32 raw
i, 0, alphaIndexPrecision).a;
}
- var pixels = raw.AsSpan;
+ var pixels = raw.AsArray;
float error = 0;
for (int i = 0; i < 16; i++)
@@ -836,14 +950,14 @@ private static float TrySubsetEndpoints(Bc7BlockType type, RawBlock4X4Rgba32 raw
}
else
{
- Span colors = stackalloc ColorYCbCrAlpha[1 << colorIndexPrecision];
+ ColorYCbCrAlpha[] colors = new ColorYCbCrAlpha[1 << colorIndexPrecision];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = new ColorYCbCrAlpha(InterpolateColor(ep0, ep1, i,
i, colorIndexPrecision, alphaIndexPrecision));
}
- var pixels = raw.AsSpan;
+ var pixels = raw.AsArray;
float error = 0;
float count = 0;
@@ -865,8 +979,8 @@ private static float TrySubsetEndpoints(Bc7BlockType type, RawBlock4X4Rgba32 raw
}
- public static void FillSubsetIndices(Bc7BlockType type, RawBlock4X4Rgba32 raw, ColorRgba32 ep0, ColorRgba32 ep1, ReadOnlySpan partitionTable, int subsetIndex,
- Span indicesToFill)
+ public static void FillSubsetIndices(Bc7BlockType type, RawBlock4X4Rgba32 raw, ColorRgba32 ep0, ColorRgba32 ep1, int[] partitionTable, int subsetIndex,
+ byte[] indicesToFill)
{
int colorIndexPrecision = GetColorIndexBitCount(type);
int alphaIndexPrecision = GetAlphaIndexBitCount(type);
@@ -877,14 +991,14 @@ public static void FillSubsetIndices(Bc7BlockType type, RawBlock4X4Rgba32 raw, C
}
else
{
- Span colors = stackalloc ColorYCbCrAlpha[1 << colorIndexPrecision];
+ ColorYCbCrAlpha[] colors = new ColorYCbCrAlpha[1 << colorIndexPrecision];
for (int i = 0; i < colors.Length; i++)
{
colors[i] = new ColorYCbCrAlpha(InterpolateColor(ep0, ep1, i,
i, colorIndexPrecision, alphaIndexPrecision));
}
- var pixels = raw.AsSpan;
+ var pixels = raw.AsArray;
for (int i = 0; i < 16; i++)
{
@@ -903,15 +1017,15 @@ public static void FillSubsetIndices(Bc7BlockType type, RawBlock4X4Rgba32 raw, C
/// Used for Modes 4 and 5
///
public static void FillAlphaColorIndices(Bc7BlockType type, RawBlock4X4Rgba32 raw, ColorRgba32 ep0, ColorRgba32 ep1,
- Span colorIndicesToFill, Span alphaIndicesToFill, int idxMode = 0)
+ byte[] colorIndicesToFill, byte[] alphaIndicesToFill, int idxMode = 0)
{
int colorIndexPrecision = GetColorIndexBitCount(type, idxMode);
int alphaIndexPrecision = GetAlphaIndexBitCount(type, idxMode);
if (type == Bc7BlockType.Type4 || type == Bc7BlockType.Type5)
{
- Span colors = stackalloc ColorYCbCr[1 << colorIndexPrecision];
- Span alphas = stackalloc byte[1 << alphaIndexPrecision];
+ ColorYCbCr[] colors = new ColorYCbCr[1 << colorIndexPrecision];
+ byte[] alphas = new byte[1 << alphaIndexPrecision];
for (int i = 0; i < colors.Length; i++)
{
@@ -925,7 +1039,7 @@ public static void FillAlphaColorIndices(Bc7BlockType type, RawBlock4X4Rgba32 ra
i, 0, alphaIndexPrecision).a;
}
- var pixels = raw.AsSpan;
+ var pixels = raw.AsArray;
for (int i = 0; i < 16; i++)
{
@@ -945,7 +1059,7 @@ public static void FillAlphaColorIndices(Bc7BlockType type, RawBlock4X4Rgba32 ra
}
public static void OptimizeSubsetEndpointsWithPBit(Bc7BlockType type, RawBlock4X4Rgba32 raw, ref ColorRgba32 ep0, ref ColorRgba32 ep1, ref byte pBit0, ref byte pBit1,
- int variation, ReadOnlySpan partitionTable, int subsetIndex, bool variatePBits, bool variateAlpha, int type4IdxMode = 0)
+ int variation, int[] partitionTable, int subsetIndex, bool variatePBits, bool variateAlpha, int type4IdxMode = 0)
{
byte colorMax = (byte)((1 << GetColorComponentPrecision(type)) - 1);
@@ -956,16 +1070,16 @@ public static void OptimizeSubsetEndpointsWithPBit(Bc7BlockType type, RawBlock4X
ExpandEndpoint(type, ep1, pBit1), partitionTable, subsetIndex, type4IdxMode
);
- ReadOnlySpan varPatternR = variateAlpha
+ int[] varPatternR = variateAlpha
? varPatternRAlpha
: varPatternRNoAlpha;
- ReadOnlySpan varPatternG = variateAlpha
+ int[] varPatternG = variateAlpha
? varPatternGAlpha
: varPatternGNoAlpha;
- ReadOnlySpan varPatternB = variateAlpha
+ int[] varPatternB = variateAlpha
? varPatternBAlpha
: varPatternBNoAlpha;
- ReadOnlySpan varPatternA = variateAlpha
+ int[] varPatternA = variateAlpha
? varPatternAAlpha
: varPatternANoAlpha;
@@ -1093,8 +1207,8 @@ public static RawBlock4X4Rgba32 RotateBlockColors(RawBlock4X4Rgba32 block, int r
}
RawBlock4X4Rgba32 rotated = new RawBlock4X4Rgba32();
- var pixels = block.AsSpan;
- var output = rotated.AsSpan;
+ var pixels = block.AsArray;
+ var output = rotated.AsArray;
for (int i = 0; i < 16; i++)
{
var c = pixels[i];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode0Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode0Encoder.cs
index 1469173..c578585 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode0Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode0Encoder.cs
@@ -18,7 +18,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
ColorRgba32[] endpoints = new ColorRgba32[6];
byte[] pBits = new byte[6];
- ReadOnlySpan partitionTable = Bc7Block.Subsets3PartitionTable[bestPartition];
+ int[] partitionTable = Bc7Block.Subsets3PartitionTable[bestPartition];
byte[] indices = new byte[16];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode1Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode1Encoder.cs
index e8d9a51..c26a1c0 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode1Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode1Encoder.cs
@@ -14,7 +14,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
ColorRgba32[] endpoints = new ColorRgba32[4];
byte[] pBits = new byte[2];
- ReadOnlySpan partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
+ int[] partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
byte[] indices = new byte[16];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode2Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode2Encoder.cs
index dee4405..7a672b9 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode2Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode2Encoder.cs
@@ -12,7 +12,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
const Bc7BlockType type = Bc7BlockType.Type2;
ColorRgba32[] endpoints = new ColorRgba32[6];
- ReadOnlySpan partitionTable = Bc7Block.Subsets3PartitionTable[bestPartition];
+ int[] partitionTable = Bc7Block.Subsets3PartitionTable[bestPartition];
byte[] indices = new byte[16];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode3Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode3Encoder.cs
index 61dd40b..5bc50da 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode3Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode3Encoder.cs
@@ -13,7 +13,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
ColorRgba32[] endpoints = new ColorRgba32[4];
byte[] pBits = new byte[4];
- ReadOnlySpan partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
+ int[] partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
byte[] indices = new byte[16];
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs
index c25df51..2e1f46d 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode4Encoder.cs
@@ -6,14 +6,14 @@ namespace BCnEncoder.Encoder.Bc7
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 };
+ private static int[] partitionTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const int subset = 0;
public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation)
{
var type = Bc7BlockType.Type4;
- Span outputs = stackalloc Bc7Block[8];
+ Bc7Block[] outputs = new Bc7Block[8];
for (int idxMode = 0; idxMode < 2; idxMode++)
{
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs
index 1b5c881..ceed6cb 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode5Encoder.cs
@@ -5,14 +5,14 @@ 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 };
+ private static int[] partitionTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const int subset = 0;
public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariation)
{
var type = Bc7BlockType.Type5;
- Span outputs = stackalloc Bc7Block[4];
+ Bc7Block[] outputs = new Bc7Block[4];
for (int rotation = 0; rotation < 4; rotation++) {
var rotatedBlock = Bc7EncodingHelpers.RotateBlockColors(block, rotation);
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode6Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode6Encoder.cs
index 844a51d..70e44e5 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode6Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode6Encoder.cs
@@ -18,7 +18,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
ColorRgba32 scaledEp1 =
Bc7EncodingHelpers.ScaleDownEndpoint(ep1, Bc7BlockType.Type6, false, out byte pBit1);
- ReadOnlySpan partitionTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ int[] partitionTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
const int subset = 0;
//Force 255 alpha if fully opaque
diff --git a/BCnEnc.Net/Encoder/Bc7/Bc7Mode7Encoder.cs b/BCnEnc.Net/Encoder/Bc7/Bc7Mode7Encoder.cs
index d58bf5e..2963eb6 100644
--- a/BCnEnc.Net/Encoder/Bc7/Bc7Mode7Encoder.cs
+++ b/BCnEnc.Net/Encoder/Bc7/Bc7Mode7Encoder.cs
@@ -13,7 +13,7 @@ public static Bc7Block EncodeBlock(RawBlock4X4Rgba32 block, int startingVariatio
ColorRgba32[] endpoints = new ColorRgba32[4];
byte[] pBits = new byte[4];
- ReadOnlySpan partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
+ int[] partitionTable = Bc7Block.Subsets2PartitionTable[bestPartition];
byte[] indices = new byte[16];
diff --git a/BCnEnc.Net/Encoder/BcEncoder.cs b/BCnEnc.Net/Encoder/BcEncoder.cs
index 3424d55..a210fc3 100644
--- a/BCnEnc.Net/Encoder/BcEncoder.cs
+++ b/BCnEnc.Net/Encoder/BcEncoder.cs
@@ -4,9 +4,6 @@
using System.IO;
using BCnEncoder.Encoder.Bc7;
using BCnEncoder.Shared;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Encoder
{
@@ -21,15 +18,6 @@ public class EncoderInputOptions
public class EncoderOutputOptions
{
- ///
- /// Whether to generate mipMaps. Default is true.
- ///
- public bool generateMipMaps = true;
- ///
- /// The maximum number of mipmap levels to generate. -1 or 0 is unbounded.
- /// Default is -1.
- ///
- public int maxMipMapLevel = -1;
///
/// The compression format to use. Default is BC1.
///
@@ -103,36 +91,19 @@ private IBcBlockEncoder GetEncoder(CompressionFormat format)
}
}
- private IRawEncoder GetRawEncoder(CompressionFormat format)
- {
- switch (format)
- {
- case CompressionFormat.R:
- return new RawLuminanceEncoder(InputOptions.luminanceAsRed);
- case CompressionFormat.RG:
- return new RawRGEncoder();
- case CompressionFormat.RGB:
- return new RawRGBEncoder();
- case CompressionFormat.RGBA:
- return new RawRGBAEncoder();
- default:
- throw new ArgumentOutOfRangeException(nameof(format), format, null);
- }
- }
-
///
/// Encodes all mipmap levels into a ktx or a dds file and writes it to the output stream.
///
- public void Encode(Image inputImage, Stream outputStream)
+ public void Encode(byte[] inputImageRgba, int inputImageWidth, int inputImageHeight, Stream outputStream)
{
if (OutputOptions.fileFormat == OutputFileFormat.Ktx)
{
- KtxFile output = EncodeToKtx(inputImage);
+ KtxFile output = EncodeToKtx(inputImageRgba, inputImageWidth, inputImageHeight);
output.Write(outputStream);
}
else if (OutputOptions.fileFormat == OutputFileFormat.Dds)
{
- DdsFile output = EncodeToDds(inputImage);
+ DdsFile output = EncodeToDds(inputImageRgba, inputImageWidth, inputImageHeight);
output.Write(outputStream);
}
}
@@ -140,73 +111,37 @@ public void Encode(Image inputImage, Stream outputStream)
///
/// Encodes all mipmap levels into a Ktx file.
///
- public KtxFile EncodeToKtx(Image inputImage)
+ public KtxFile EncodeToKtx(byte[] inputImageRgba, int inputImageWidth, int inputImageHeight)
{
KtxFile output;
IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
- if (OutputOptions.format.IsCompressedFormat())
+
+ compressedEncoder = GetEncoder(OutputOptions.format);
+ if (compressedEncoder == null)
{
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
- output = new KtxFile(
- KtxHeader.InitializeCompressed(inputImage.Width, inputImage.Height,
- compressedEncoder.GetInternalFormat(),
- compressedEncoder.GetBaseInternalFormat()));
+ throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
}
- else
- {
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
- output = new KtxFile(
- KtxHeader.InitializeUncompressed(inputImage.Width, inputImage.Height,
- uncompressedEncoder.GetGlType(),
- uncompressedEncoder.GetGlFormat(),
- uncompressedEncoder.GetGlTypeSize(),
- uncompressedEncoder.GetInternalFormat(),
- uncompressedEncoder.GetBaseInternalFormat()));
+ output = new KtxFile(
+ KtxHeader.InitializeCompressed(inputImageWidth, inputImageHeight,
+ compressedEncoder.GetInternalFormat(),
+ compressedEncoder.GetBaseInternalFormat()));
- }
+ uint numMipMaps = 1;
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
- {
- numMipMaps = 1;
- }
-
- var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps);
-
- for (int i = 0; i < numMipMaps; i++)
+ byte[] encoded = null;
+ if (OutputOptions.format.IsCompressedFormat())
{
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[i].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- output.MipMaps.Add(new KtxMipmap((uint)encoded.Length,
- (uint)inputImage.Width,
- (uint)inputImage.Height, 1));
- output.MipMaps[i].Faces[0] = new KtxMipFace(encoded,
- (uint)inputImage.Width,
- (uint)inputImage.Height);
+ var blocks = ImageToBlocks.ImageTo4X4(inputImageRgba, inputImageWidth, inputImageHeight, out int blocksWidth, out int blocksHeight);
+ encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ !Debugger.IsAttached && Options.multiThreaded);
}
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
+ output.MipMaps.Add(new KtxMipmap((uint)encoded.Length,
+ (uint)inputImageWidth,
+ (uint)inputImageHeight, 1));
+ output.MipMaps[0].Faces[0] = new KtxMipFace(encoded,
+ (uint)inputImageWidth,
+ (uint)inputImageHeight);
output.Header.NumberOfFaces = 1;
output.Header.NumberOfMipmapLevels = numMipMaps;
@@ -215,79 +150,43 @@ public KtxFile EncodeToKtx(Image inputImage)
}
///
- /// Encodes all mipmap levels into a Ktx file.
+ /// Encodes Rgba image into a Dds file.
///
- public DdsFile EncodeToDds(Image inputImage)
+ public DdsFile EncodeToDds(byte[] inputImageRgba, int inputImageWidth, int inputImageHeight)
{
DdsFile output;
IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
-
- var (ddsHeader, dxt10Header) = DdsHeader.InitializeCompressed(inputImage.Width, inputImage.Height,
- compressedEncoder.GetDxgiFormat());
- output = new DdsFile(ddsHeader, dxt10Header);
-
- if (OutputOptions.ddsBc1WriteAlphaFlag &&
- OutputOptions.format == CompressionFormat.BC1WithAlpha)
- {
- output.Header.ddsPixelFormat.dwFlags |= PixelFormatFlags.DDPF_ALPHAPIXELS;
- }
- }
- else
+ compressedEncoder = GetEncoder(OutputOptions.format);
+ if (compressedEncoder == null)
{
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
- var ddsHeader = DdsHeader.InitializeUncompressed(inputImage.Width, inputImage.Height,
- uncompressedEncoder.GetDxgiFormat());
- output = new DdsFile(ddsHeader);
+ throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
}
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
+ DdsHeaderDxt10 dxt10Header;
+ var ddsHeader = DdsHeader.InitializeCompressed(inputImageWidth, inputImageHeight,
+ compressedEncoder.GetDxgiFormat(), out dxt10Header);
+
+ output = new DdsFile(ddsHeader, dxt10Header);
+
+ if (OutputOptions.ddsBc1WriteAlphaFlag &&
+ OutputOptions.format == CompressionFormat.BC1WithAlpha)
{
- numMipMaps = 1;
+ output.Header.ddsPixelFormat.dwFlags |= PixelFormatFlags.DDPF_ALPHAPIXELS;
}
- var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps);
+ uint numMipMaps = 1;
- for (int mip = 0; mip < numMipMaps; mip++)
- {
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[mip].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[mip].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- if (mip == 0)
- {
- output.Faces.Add(new DdsFace((uint)inputImage.Width, (uint)inputImage.Height,
- (uint)encoded.Length, (int)numMipMaps));
- }
-
- output.Faces[0].MipMaps[mip] = new DdsMipMap(encoded,
- (uint)inputImage.Width,
- (uint)inputImage.Height);
- }
+ var blocks = ImageToBlocks.ImageTo4X4(inputImageRgba, inputImageWidth, inputImageHeight, out int blocksWidth, out int blocksHeight);
+ var encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ !Debugger.IsAttached && Options.multiThreaded);
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
+ output.Faces.Add(new DdsFace((uint)inputImageWidth, (uint)inputImageHeight,
+ (uint)encoded.Length, (int)numMipMaps));
+
+
+ output.Faces[0].MipMaps[0] = new DdsMipMap(encoded,
+ (uint)inputImageWidth,
+ (uint)inputImageHeight);
output.Header.dwMipMapCount = numMipMaps;
if (numMipMaps > 1)
@@ -299,363 +198,324 @@ public DdsFile EncodeToDds(Image inputImage)
}
///
- /// Encodes all mipmap levels into a list of byte buffers.
+ /// Encodes an RGBA byte buffer to raw compressed bytes
///
- public List EncodeToRawBytes(Image inputImage)
+ public byte[] EncodeToRawBytes(byte[] inputImageRgba, int inputImageWidth, int inputImageHeight, out int outputImageWidth, out int outputImageHeight)
{
- List output = new List();
- IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
-
- }
- else
- {
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
-
- }
-
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
+ IBcBlockEncoder compressedEncoder = GetEncoder(OutputOptions.format);
+ if (compressedEncoder == null)
{
- numMipMaps = 1;
+ throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
}
- var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps);
+ var blocks = ImageToBlocks.ImageTo4X4(inputImageRgba, inputImageWidth, inputImageHeight, out int blocksWidth, out int blocksHeight);
+ var encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ !Debugger.IsAttached && Options.multiThreaded);
+ outputImageWidth = blocksWidth * 4;
+ outputImageHeight = blocksHeight * 4;
- for (int i = 0; i < numMipMaps; i++)
- {
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[i].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- output.Add(encoded);
- }
-
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
-
- return output;
+ return encoded;
}
///
/// Encodes a single mip level of the input image to a byte buffer.
///
- public byte[] EncodeToRawBytes(Image inputImage, int mipLevel, out int mipWidth, out int mipHeight)
- {
- if (mipLevel < 0)
- {
- throw new ArgumentException($"{nameof(mipLevel)} cannot be less than zero.");
- }
-
- IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
- }
- else
- {
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
-
- }
-
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
- {
- numMipMaps = 1;
- }
-
- var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps);
-
- if (mipLevel > numMipMaps - 1)
- {
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
- throw new ArgumentException($"{nameof(mipLevel)} cannot be more than number of mipmaps");
- }
-
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[mipLevel].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[mipLevel].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- mipWidth = mipChain[mipLevel].Width;
- mipHeight = mipChain[mipLevel].Height;
-
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
-
- return encoded;
- }
+ //public byte[] EncodeToRawBytes(Image inputImage, int mipLevel, out int mipWidth, out int mipHeight)
+ //{
+ // if (mipLevel < 0)
+ // {
+ // throw new ArgumentException($"{nameof(mipLevel)} cannot be less than zero.");
+ // }
+
+ // IBcBlockEncoder compressedEncoder = null;
+ // IRawEncoder uncompressedEncoder = null;
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // compressedEncoder = GetEncoder(OutputOptions.format);
+ // if (compressedEncoder == null)
+ // {
+ // throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
+ // }
+ // }
+ // else
+ // {
+ // uncompressedEncoder = GetRawEncoder(OutputOptions.format);
+
+ // }
+
+ // uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
+ // if (!OutputOptions.generateMipMaps)
+ // {
+ // numMipMaps = 1;
+ // }
+
+ // var mipChain = MipMapper.GenerateMipChain(inputImage, ref numMipMaps);
+
+ // if (mipLevel > numMipMaps - 1)
+ // {
+ // foreach (var image in mipChain)
+ // {
+ // image.Dispose();
+ // }
+ // throw new ArgumentException($"{nameof(mipLevel)} cannot be more than number of mipmaps");
+ // }
+
+ // byte[] encoded = null;
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // var blocks = ImageToBlocks.ImageTo4X4(mipChain[mipLevel].Frames[0], out int blocksWidth, out int blocksHeight);
+ // encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ // !Debugger.IsAttached && Options.multiThreaded);
+ // }
+ // else
+ // {
+ // if (!mipChain[mipLevel].TryGetSinglePixelSpan(out var mipPixels)) {
+ // throw new Exception("Cannot get pixel span.");
+ // }
+ // encoded = uncompressedEncoder.Encode(mipPixels);
+ // }
+
+ // mipWidth = mipChain[mipLevel].Width;
+ // mipHeight = mipChain[mipLevel].Height;
+
+ // foreach (var image in mipChain)
+ // {
+ // image.Dispose();
+ // }
+
+ // return encoded;
+ //}
///
/// 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
///
- public void EncodeCubeMap(Image right, Image left, Image top, Image down,
- Image back, Image front, Stream outputStream)
- {
- if (OutputOptions.fileFormat == OutputFileFormat.Ktx)
- {
- KtxFile output = EncodeCubeMapToKtx(right, left, top, down, back, front);
- output.Write(outputStream);
- }
- else if (OutputOptions.fileFormat == OutputFileFormat.Dds)
- {
- DdsFile output = EncodeCubeMapToDds(right, left, top, down, back, front);
- output.Write(outputStream);
- }
- }
+ //public void EncodeCubeMap(Image right, Image left, Image top, Image down,
+ // Image back, Image front, Stream outputStream)
+ //{
+ // if (OutputOptions.fileFormat == OutputFileFormat.Ktx)
+ // {
+ // KtxFile output = EncodeCubeMapToKtx(right, left, top, down, back, front);
+ // output.Write(outputStream);
+ // }
+ // else if (OutputOptions.fileFormat == OutputFileFormat.Dds)
+ // {
+ // DdsFile output = EncodeCubeMapToDds(right, left, top, down, back, front);
+ // output.Write(outputStream);
+ // }
+ //}
///
/// Encodes all cubemap faces and mipmap levels into a Ktx file.
/// Order is +X, -X, +Y, -Y, +Z, -Z. Back maps to positive Z and front to negative Z.
///
- public KtxFile EncodeCubeMapToKtx(Image right, Image left, Image top, Image down,
- Image back, Image front)
- {
- KtxFile output;
- IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
-
- if (right.Width != left.Width || right.Width != top.Width || right.Width != down.Width
- || right.Width != back.Width || right.Width != front.Width ||
- right.Height != left.Height || right.Height != top.Height || right.Height != down.Height
- || right.Height != back.Height || right.Height != front.Height)
- {
- throw new ArgumentException("All input images of a cubemap should be the same size.");
- }
-
- Image[] faces = new[] { right, left, top, down, back, front };
-
- if (OutputOptions.format.IsCompressedFormat())
- {
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
- output = new KtxFile(
- KtxHeader.InitializeCompressed(right.Width, right.Height,
- compressedEncoder.GetInternalFormat(),
- compressedEncoder.GetBaseInternalFormat()));
- }
- else
- {
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
- output = new KtxFile(
- KtxHeader.InitializeUncompressed(right.Width, right.Height,
- uncompressedEncoder.GetGlType(),
- uncompressedEncoder.GetGlFormat(),
- uncompressedEncoder.GetGlTypeSize(),
- uncompressedEncoder.GetInternalFormat(),
- uncompressedEncoder.GetBaseInternalFormat()));
-
- }
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
- {
- numMipMaps = 1;
- }
-
- uint mipLength = MipMapper.CalculateMipChainLength(right.Width, right.Height, numMipMaps);
- for (uint i = 0; i < mipLength; i++)
- {
- output.MipMaps.Add(new KtxMipmap(0, 0, 0, (uint)faces.Length));
- }
-
- for (int f = 0; f < faces.Length; f++)
- {
-
- var mipChain = MipMapper.GenerateMipChain(faces[f], ref numMipMaps);
-
- for (int i = 0; i < numMipMaps; i++)
- {
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[i].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- if (f == 0)
- {
- output.MipMaps[i] = new KtxMipmap((uint)encoded.Length,
- (uint)mipChain[i].Width,
- (uint)mipChain[i].Height, (uint)faces.Length);
- }
-
- output.MipMaps[i].Faces[f] = new KtxMipFace(encoded,
- (uint)mipChain[i].Width,
- (uint)mipChain[i].Height);
- }
-
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
- }
-
- output.Header.NumberOfFaces = (uint)faces.Length;
- output.Header.NumberOfMipmapLevels = mipLength;
-
- return output;
- }
+ //public KtxFile EncodeCubeMapToKtx(Image right, Image left, Image top, Image down,
+ // Image back, Image front)
+ //{
+ // KtxFile output;
+ // IBcBlockEncoder compressedEncoder = null;
+ // IRawEncoder uncompressedEncoder = null;
+
+ // if (right.Width != left.Width || right.Width != top.Width || right.Width != down.Width
+ // || right.Width != back.Width || right.Width != front.Width ||
+ // right.Height != left.Height || right.Height != top.Height || right.Height != down.Height
+ // || right.Height != back.Height || right.Height != front.Height)
+ // {
+ // throw new ArgumentException("All input images of a cubemap should be the same size.");
+ // }
+
+ // Image[] faces = new[] { right, left, top, down, back, front };
+
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // compressedEncoder = GetEncoder(OutputOptions.format);
+ // if (compressedEncoder == null)
+ // {
+ // throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
+ // }
+ // output = new KtxFile(
+ // KtxHeader.InitializeCompressed(right.Width, right.Height,
+ // compressedEncoder.GetInternalFormat(),
+ // compressedEncoder.GetBaseInternalFormat()));
+ // }
+ // else
+ // {
+ // uncompressedEncoder = GetRawEncoder(OutputOptions.format);
+ // output = new KtxFile(
+ // KtxHeader.InitializeUncompressed(right.Width, right.Height,
+ // uncompressedEncoder.GetGlType(),
+ // uncompressedEncoder.GetGlFormat(),
+ // uncompressedEncoder.GetGlTypeSize(),
+ // uncompressedEncoder.GetInternalFormat(),
+ // uncompressedEncoder.GetBaseInternalFormat()));
+
+ // }
+ // uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
+ // if (!OutputOptions.generateMipMaps)
+ // {
+ // numMipMaps = 1;
+ // }
+
+ // uint mipLength = MipMapper.CalculateMipChainLength(right.Width, right.Height, numMipMaps);
+ // for (uint i = 0; i < mipLength; i++)
+ // {
+ // output.MipMaps.Add(new KtxMipmap(0, 0, 0, (uint)faces.Length));
+ // }
+
+ // for (int f = 0; f < faces.Length; f++)
+ // {
+
+ // var mipChain = MipMapper.GenerateMipChain(faces[f], ref numMipMaps);
+
+ // for (int i = 0; i < numMipMaps; i++)
+ // {
+ // byte[] encoded = null;
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // var blocks = ImageToBlocks.ImageTo4X4(mipChain[i].Frames[0], out int blocksWidth, out int blocksHeight);
+ // encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ // !Debugger.IsAttached && Options.multiThreaded);
+ // }
+ // else
+ // {
+ // if (!mipChain[i].TryGetSinglePixelSpan(out var mipPixels)) {
+ // throw new Exception("Cannot get pixel span.");
+ // }
+ // encoded = uncompressedEncoder.Encode(mipPixels);
+ // }
+
+ // if (f == 0)
+ // {
+ // output.MipMaps[i] = new KtxMipmap((uint)encoded.Length,
+ // (uint)mipChain[i].Width,
+ // (uint)mipChain[i].Height, (uint)faces.Length);
+ // }
+
+ // output.MipMaps[i].Faces[f] = new KtxMipFace(encoded,
+ // (uint)mipChain[i].Width,
+ // (uint)mipChain[i].Height);
+ // }
+
+ // foreach (var image in mipChain)
+ // {
+ // image.Dispose();
+ // }
+ // }
+
+ // output.Header.NumberOfFaces = (uint)faces.Length;
+ // output.Header.NumberOfMipmapLevels = mipLength;
+
+ // return output;
+ //}
///
/// Encodes all cubemap faces and mipmap levels into a Dds file.
/// Order is +X, -X, +Y, -Y, +Z, -Z. Back maps to positive Z and front to negative Z.
///
- public DdsFile EncodeCubeMapToDds(Image right, Image left, Image top, Image down,
- Image back, Image front)
- {
- DdsFile output;
- IBcBlockEncoder compressedEncoder = null;
- IRawEncoder uncompressedEncoder = null;
-
- if (right.Width != left.Width || right.Width != top.Width || right.Width != down.Width
- || right.Width != back.Width || right.Width != front.Width ||
- right.Height != left.Height || right.Height != top.Height || right.Height != down.Height
- || right.Height != back.Height || right.Height != front.Height)
- {
- throw new ArgumentException("All input images of a cubemap should be the same size.");
- }
-
- Image[] faces = new[] { right, left, top, down, back, front };
-
- if (OutputOptions.format.IsCompressedFormat())
- {
- compressedEncoder = GetEncoder(OutputOptions.format);
- if (compressedEncoder == null)
- {
- throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
- }
-
- var (ddsHeader, dxt10Header) = DdsHeader.InitializeCompressed(right.Width, right.Height,
- compressedEncoder.GetDxgiFormat());
- output = new DdsFile(ddsHeader, dxt10Header);
-
- if (OutputOptions.ddsBc1WriteAlphaFlag &&
- OutputOptions.format == CompressionFormat.BC1WithAlpha)
- {
- output.Header.ddsPixelFormat.dwFlags |= PixelFormatFlags.DDPF_ALPHAPIXELS;
- }
- }
- else
- {
- uncompressedEncoder = GetRawEncoder(OutputOptions.format);
- var ddsHeader = DdsHeader.InitializeUncompressed(right.Width, right.Height,
- uncompressedEncoder.GetDxgiFormat());
- output = new DdsFile(ddsHeader);
- }
-
- uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
- if (!OutputOptions.generateMipMaps)
- {
- numMipMaps = 1;
- }
-
- for (int f = 0; f < faces.Length; f++)
- {
-
- var mipChain = MipMapper.GenerateMipChain(faces[f], ref numMipMaps);
-
-
- for (int mip = 0; mip < numMipMaps; mip++)
- {
- byte[] encoded = null;
- if (OutputOptions.format.IsCompressedFormat())
- {
- var blocks = ImageToBlocks.ImageTo4X4(mipChain[mip].Frames[0], out int blocksWidth, out int blocksHeight);
- encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
- !Debugger.IsAttached && Options.multiThreaded);
- }
- else
- {
- if (!mipChain[mip].TryGetSinglePixelSpan(out var mipPixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- encoded = uncompressedEncoder.Encode(mipPixels);
- }
-
- if (mip == 0)
- {
- output.Faces.Add(new DdsFace((uint)mipChain[mip].Width, (uint)mipChain[mip].Height,
- (uint)encoded.Length, mipChain.Count));
- }
-
- output.Faces[f].MipMaps[mip] = new DdsMipMap(encoded,
- (uint)mipChain[mip].Width,
- (uint)mipChain[mip].Height);
- }
-
- foreach (var image in mipChain)
- {
- image.Dispose();
- }
- }
-
- output.Header.dwCaps |= HeaderCaps.DDSCAPS_COMPLEX;
- output.Header.dwMipMapCount = numMipMaps;
- if (numMipMaps > 1)
- {
- output.Header.dwCaps |= HeaderCaps.DDSCAPS_MIPMAP;
- }
- output.Header.dwCaps2 |= HeaderCaps2.DDSCAPS2_CUBEMAP |
- HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEX |
- HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEX |
- HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEY |
- HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEY |
- HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEZ |
- HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEZ;
-
- return output;
- }
+ //public DdsFile EncodeCubeMapToDds(Image right, Image left, Image top, Image down,
+ // Image back, Image front)
+ //{
+ // DdsFile output;
+ // IBcBlockEncoder compressedEncoder = null;
+ // IRawEncoder uncompressedEncoder = null;
+
+ // if (right.Width != left.Width || right.Width != top.Width || right.Width != down.Width
+ // || right.Width != back.Width || right.Width != front.Width ||
+ // right.Height != left.Height || right.Height != top.Height || right.Height != down.Height
+ // || right.Height != back.Height || right.Height != front.Height)
+ // {
+ // throw new ArgumentException("All input images of a cubemap should be the same size.");
+ // }
+
+ // Image[] faces = new[] { right, left, top, down, back, front };
+
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // compressedEncoder = GetEncoder(OutputOptions.format);
+ // if (compressedEncoder == null)
+ // {
+ // throw new NotSupportedException($"This format is not supported: {OutputOptions.format}");
+ // }
+
+ // var (ddsHeader, dxt10Header) = DdsHeader.InitializeCompressed(right.Width, right.Height,
+ // compressedEncoder.GetDxgiFormat());
+ // output = new DdsFile(ddsHeader, dxt10Header);
+
+ // if (OutputOptions.ddsBc1WriteAlphaFlag &&
+ // OutputOptions.format == CompressionFormat.BC1WithAlpha)
+ // {
+ // output.Header.ddsPixelFormat.dwFlags |= PixelFormatFlags.DDPF_ALPHAPIXELS;
+ // }
+ // }
+ // else
+ // {
+ // uncompressedEncoder = GetRawEncoder(OutputOptions.format);
+ // var ddsHeader = DdsHeader.InitializeUncompressed(right.Width, right.Height,
+ // uncompressedEncoder.GetDxgiFormat());
+ // output = new DdsFile(ddsHeader);
+ // }
+
+ // uint numMipMaps = (uint)OutputOptions.maxMipMapLevel;
+ // if (!OutputOptions.generateMipMaps)
+ // {
+ // numMipMaps = 1;
+ // }
+
+ // for (int f = 0; f < faces.Length; f++)
+ // {
+
+ // var mipChain = MipMapper.GenerateMipChain(faces[f], ref numMipMaps);
+
+
+ // for (int mip = 0; mip < numMipMaps; mip++)
+ // {
+ // byte[] encoded = null;
+ // if (OutputOptions.format.IsCompressedFormat())
+ // {
+ // var blocks = ImageToBlocks.ImageTo4X4(mipChain[mip].Frames[0], out int blocksWidth, out int blocksHeight);
+ // encoded = compressedEncoder.Encode(blocks, blocksWidth, blocksHeight, OutputOptions.quality,
+ // !Debugger.IsAttached && Options.multiThreaded);
+ // }
+ // else
+ // {
+ // if (!mipChain[mip].TryGetSinglePixelSpan(out var mipPixels)) {
+ // throw new Exception("Cannot get pixel span.");
+ // }
+ // encoded = uncompressedEncoder.Encode(mipPixels);
+ // }
+
+ // if (mip == 0)
+ // {
+ // output.Faces.Add(new DdsFace((uint)mipChain[mip].Width, (uint)mipChain[mip].Height,
+ // (uint)encoded.Length, mipChain.Count));
+ // }
+
+ // output.Faces[f].MipMaps[mip] = new DdsMipMap(encoded,
+ // (uint)mipChain[mip].Width,
+ // (uint)mipChain[mip].Height);
+ // }
+
+ // foreach (var image in mipChain)
+ // {
+ // image.Dispose();
+ // }
+ // }
+
+ // output.Header.dwCaps |= HeaderCaps.DDSCAPS_COMPLEX;
+ // output.Header.dwMipMapCount = numMipMaps;
+ // if (numMipMaps > 1)
+ // {
+ // output.Header.dwCaps |= HeaderCaps.DDSCAPS_MIPMAP;
+ // }
+ // output.Header.dwCaps2 |= HeaderCaps2.DDSCAPS2_CUBEMAP |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEX |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEX |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEY |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEY |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_POSITIVEZ |
+ // HeaderCaps2.DDSCAPS2_CUBEMAP_NEGATIVEZ;
+
+ // return output;
+ //}
}
}
diff --git a/BCnEnc.Net/Encoder/ColorChooser.cs b/BCnEnc.Net/Encoder/ColorChooser.cs
index ae4ac45..8f0429d 100644
--- a/BCnEnc.Net/Encoder/ColorChooser.cs
+++ b/BCnEnc.Net/Encoder/ColorChooser.cs
@@ -1,27 +1,26 @@
using System;
using BCnEncoder.Shared;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Encoder
{
internal static class ColorChooser
{
- public static int ChooseClosestColor4(ReadOnlySpan colors, Rgba32 color, float rWeight, float gWeight, float bWeight, out float error)
+ public static int ChooseClosestColor4(ColorRgb24[] colors, Rgba32 color, float rWeight, float gWeight, float bWeight, out float error)
{
- ReadOnlySpan d = stackalloc float[4] {
- MathF.Abs(colors[0].r - color.R) * rWeight
- + MathF.Abs(colors[0].g - color.G) * gWeight
- + MathF.Abs(colors[0].b - color.B) * bWeight,
- MathF.Abs(colors[1].r - color.R) * rWeight
- + MathF.Abs(colors[1].g - color.G) * gWeight
- + MathF.Abs(colors[1].b - color.B) * bWeight,
- MathF.Abs(colors[2].r - color.R) * rWeight
- + MathF.Abs(colors[2].g - color.G) * gWeight
- + MathF.Abs(colors[2].b - color.B) * bWeight,
- MathF.Abs(colors[3].r - color.R) * rWeight
- + MathF.Abs(colors[3].g - color.G) * gWeight
- + MathF.Abs(colors[3].b - color.B) * bWeight,
+ float[] d = new float[4] {
+ Math.Abs(colors[0].r - color.R) * rWeight
+ + Math.Abs(colors[0].g - color.G) * gWeight
+ + Math.Abs(colors[0].b - color.B) * bWeight,
+ Math.Abs(colors[1].r - color.R) * rWeight
+ + Math.Abs(colors[1].g - color.G) * gWeight
+ + Math.Abs(colors[1].b - color.B) * bWeight,
+ Math.Abs(colors[2].r - color.R) * rWeight
+ + Math.Abs(colors[2].g - color.G) * gWeight
+ + Math.Abs(colors[2].b - color.B) * bWeight,
+ Math.Abs(colors[3].r - color.R) * rWeight
+ + Math.Abs(colors[3].g - color.G) * gWeight
+ + Math.Abs(colors[3].b - color.B) * bWeight,
};
int b0 = d[0] > d[3] ? 1 : 0;
@@ -40,7 +39,7 @@ public static int ChooseClosestColor4(ReadOnlySpan colors, Rgba32 co
}
- public static int ChooseClosestColor4AlphaCutoff(ReadOnlySpan colors, Rgba32 color, float rWeight, float gWeight, float bWeight, int alphaCutoff, bool hasAlpha, out float error)
+ public static int ChooseClosestColor4AlphaCutoff(ColorRgb24[] colors, Rgba32 color, float rWeight, float gWeight, float bWeight, int alphaCutoff, bool hasAlpha, out float error)
{
if (hasAlpha && color.A < alphaCutoff)
@@ -49,21 +48,21 @@ public static int ChooseClosestColor4AlphaCutoff(ReadOnlySpan colors
return 3;
}
- ReadOnlySpan d = stackalloc float[4] {
- MathF.Abs(colors[0].r - color.R) * rWeight
- + MathF.Abs(colors[0].g - color.G) * gWeight
- + MathF.Abs(colors[0].b - color.B) * bWeight,
- MathF.Abs(colors[1].r - color.R) * rWeight
- + MathF.Abs(colors[1].g - color.G) * gWeight
- + MathF.Abs(colors[1].b - color.B) * bWeight,
- MathF.Abs(colors[2].r - color.R) * rWeight
- + MathF.Abs(colors[2].g - color.G) * gWeight
- + MathF.Abs(colors[2].b - color.B) * bWeight,
+ float[] d = new float[4] {
+ Math.Abs(colors[0].r - color.R) * rWeight
+ + Math.Abs(colors[0].g - color.G) * gWeight
+ + Math.Abs(colors[0].b - color.B) * bWeight,
+ Math.Abs(colors[1].r - color.R) * rWeight
+ + Math.Abs(colors[1].g - color.G) * gWeight
+ + Math.Abs(colors[1].b - color.B) * bWeight,
+ Math.Abs(colors[2].r - color.R) * rWeight
+ + Math.Abs(colors[2].g - color.G) * gWeight
+ + Math.Abs(colors[2].b - color.B) * bWeight,
hasAlpha ? 999 :
- MathF.Abs(colors[3].r - color.R) * rWeight
- + MathF.Abs(colors[3].g - color.G) * gWeight
- + MathF.Abs(colors[3].b - color.B) * bWeight,
+ Math.Abs(colors[3].r - color.R) * rWeight
+ + Math.Abs(colors[3].g - color.G) * gWeight
+ + Math.Abs(colors[3].b - color.B) * bWeight,
};
int b0 = d[0] > d[2] ? 1 : 0;
@@ -80,7 +79,7 @@ public static int ChooseClosestColor4AlphaCutoff(ReadOnlySpan colors
return idx;
}
- public static int ChooseClosestColor(Span colors, Rgba32 color)
+ public static int ChooseClosestColor(ColorRgb24[] colors, Rgba32 color)
{
int closest = 0;
int closestError =
@@ -103,7 +102,7 @@ public static int ChooseClosestColor(Span colors, Rgba32 color)
return closest;
}
- public static int ChooseClosestColor(Span colors, Rgba32 color)
+ public static int ChooseClosestColor(ColorRgba32[] colors, Rgba32 color)
{
int closest = 0;
int closestError =
@@ -128,7 +127,7 @@ public static int ChooseClosestColor(Span colors, Rgba32 color)
return closest;
}
- public static int ChooseClosestColorAlphaCutOff(Span colors, Rgba32 color, byte alphaCutOff = 255 / 2)
+ public static int ChooseClosestColorAlphaCutOff(ColorRgba32[] colors, Rgba32 color, byte alphaCutOff = 255 / 2)
{
if (color.A <= alphaCutOff)
{
@@ -157,7 +156,7 @@ public static int ChooseClosestColorAlphaCutOff(Span colors, Rgba32
return closest;
}
- public static int ChooseClosestColor(Span colors, ColorYCbCr color, float luminanceMultiplier = 4)
+ public static int ChooseClosestColor(ColorYCbCr[] colors, ColorYCbCr color, float luminanceMultiplier = 4)
{
int closest = 0;
float closestError = 0;
@@ -165,9 +164,9 @@ public static int ChooseClosestColor(Span colors, ColorYCbCr color,
for (int i = 0; i < colors.Length; i++)
{
- float error = MathF.Abs(colors[i].y - color.y) * luminanceMultiplier
- + MathF.Abs(colors[i].cb - color.cb)
- + MathF.Abs(colors[i].cr - color.cr);
+ float error = Math.Abs(colors[i].y - color.y) * luminanceMultiplier
+ + Math.Abs(colors[i].cb - color.cb)
+ + Math.Abs(colors[i].cr - color.cr);
if (first)
{
closestError = error;
@@ -182,7 +181,7 @@ public static int ChooseClosestColor(Span colors, ColorYCbCr color,
return closest;
}
- public static int ChooseClosestColor(Span colors, Rgba32 color, float luminanceMultiplier = 4)
+ public static int ChooseClosestColor(ColorYCbCr[] colors, Rgba32 color, float luminanceMultiplier = 4)
=> ChooseClosestColor(colors, new ColorYCbCr(color), luminanceMultiplier);
}
}
diff --git a/BCnEnc.Net/Encoder/ColorVariationGenerator.cs b/BCnEnc.Net/Encoder/ColorVariationGenerator.cs
index 96185ed..99de392 100644
--- a/BCnEnc.Net/Encoder/ColorVariationGenerator.cs
+++ b/BCnEnc.Net/Encoder/ColorVariationGenerator.cs
@@ -14,7 +14,7 @@ internal static class ColorVariationGenerator
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;
- public static (ColorRgb565, ColorRgb565) Variate565(ColorRgb565 c0, ColorRgb565 c1, int i) {
+ public static void Variate565(ColorRgb565 c0, ColorRgb565 c1, int i, out ColorRgb565 outC0, out ColorRgb565 outC1) {
int idx = i % varPatternEp0R.Length;
var newEp0 = new ColorRgb565();
var newEp1 = new ColorRgb565();
@@ -27,7 +27,8 @@ public static (ColorRgb565, ColorRgb565) Variate565(ColorRgb565 c0, ColorRgb565
newEp1.RawG = ByteHelper.ClampToByte(c1.RawG + varPatternEp1G[idx]);
newEp1.RawB = ByteHelper.ClampToByte(c1.RawB + varPatternEp1B[idx]);
- return (newEp0, newEp1);
+ outC0 = newEp0;
+ outC1 = newEp1;
}
diff --git a/BCnEnc.Net/Encoder/IBcBlockEncoder.cs b/BCnEnc.Net/Encoder/IBcBlockEncoder.cs
index 1801c16..4b6b170 100644
--- a/BCnEnc.Net/Encoder/IBcBlockEncoder.cs
+++ b/BCnEnc.Net/Encoder/IBcBlockEncoder.cs
@@ -1,6 +1,5 @@
using System;
using BCnEncoder.Shared;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Encoder
{
diff --git a/BCnEnc.Net/Encoder/RawEncoders.cs b/BCnEnc.Net/Encoder/RawEncoders.cs
deleted file mode 100644
index 0b5c16a..0000000
--- a/BCnEnc.Net/Encoder/RawEncoders.cs
+++ /dev/null
@@ -1,142 +0,0 @@
-using System;
-using BCnEncoder.Shared;
-using SixLabors.ImageSharp.PixelFormats;
-
-namespace BCnEncoder.Encoder
-{
- internal interface IRawEncoder
- {
- byte[] Encode(ReadOnlySpan pixels);
- GlInternalFormat GetInternalFormat();
- GLFormat GetBaseInternalFormat();
- GLFormat GetGlFormat();
- GLType GetGlType();
- uint GetGlTypeSize();
- DXGI_FORMAT GetDxgiFormat();
- }
-
- internal class RawLuminanceEncoder : IRawEncoder {
- private readonly bool useLuminance;
-
- public RawLuminanceEncoder(bool useLuminance) {
- this.useLuminance = useLuminance;
- }
-
- public byte[] Encode(ReadOnlySpan pixels) {
- byte[] output = new byte[pixels.Length];
- for (int i = 0; i < pixels.Length; i++) {
- if (useLuminance) {
- output[i] = (byte)(new ColorYCbCr(pixels[i]).y * 255);
- }
- else {
- output[i] = pixels[i].R;
- }
-
- }
- return output;
- }
-
- public GlInternalFormat GetInternalFormat()
- => GlInternalFormat.GL_R8;
-
- public GLFormat GetBaseInternalFormat()
- => GLFormat.GL_RED;
-
- public GLFormat GetGlFormat() => GLFormat.GL_RED;
-
- public GLType GetGlType()
- => GLType.GL_BYTE;
-
- public uint GetGlTypeSize()
- => 1;
-
- public DXGI_FORMAT GetDxgiFormat() => DXGI_FORMAT.DXGI_FORMAT_R8_UNORM;
- }
-
- internal class RawRGEncoder : IRawEncoder
- {
- public byte[] Encode(ReadOnlySpan pixels) {
- byte[] output = new byte[pixels.Length * 2];
- for (int i = 0; i < pixels.Length; i++) {
- output[i * 2] = pixels[i].R;
- output[i * 2 + 1] = pixels[i].G;
- }
- return output;
- }
-
- public GlInternalFormat GetInternalFormat()
- => GlInternalFormat.GL_RG8;
-
- public GLFormat GetBaseInternalFormat()
- => GLFormat.GL_RG;
-
- public GLFormat GetGlFormat() => GLFormat.GL_RG;
-
- public GLType GetGlType()
- => GLType.GL_BYTE;
-
- public uint GetGlTypeSize()
- => 1;
-
- public DXGI_FORMAT GetDxgiFormat() => DXGI_FORMAT.DXGI_FORMAT_R8G8_UNORM;
- }
-
- internal class RawRGBEncoder : IRawEncoder
- {
- public byte[] Encode(ReadOnlySpan pixels) {
- byte[] output = new byte[pixels.Length * 3];
- for (int i = 0; i < pixels.Length; i++) {
- output[i * 3] = pixels[i].R;
- output[i * 3 + 1] = pixels[i].G;
- output[i * 3 + 2] = pixels[i].B;
- }
- return output;
- }
-
- public GlInternalFormat GetInternalFormat()
- => GlInternalFormat.GL_RGB8;
-
- public GLFormat GetBaseInternalFormat()
- => GLFormat.GL_RGB;
-
- public GLFormat GetGlFormat() => GLFormat.GL_RGB;
-
- public GLType GetGlType()
- => GLType.GL_BYTE;
-
- public uint GetGlTypeSize()
- => 1;
-
- public DXGI_FORMAT GetDxgiFormat() => throw new NotSupportedException("RGB format is not supported for dds files.");
- }
-
- internal class RawRGBAEncoder : IRawEncoder
- {
- public byte[] Encode(ReadOnlySpan pixels) {
- byte[] output = new byte[pixels.Length * 4];
- for (int i = 0; i < pixels.Length; i++) {
- output[i * 4] = pixels[i].R;
- output[i * 4 + 1] = pixels[i].G;
- output[i * 4 + 2] = pixels[i].B;
- output[i * 4 + 3] = pixels[i].A;
- }
- return output;
- }
-
- public GlInternalFormat GetInternalFormat()
- => GlInternalFormat.GL_RGBA8;
-
- public GLFormat GetBaseInternalFormat()
- => GLFormat.GL_RGBA;
-
- public GLFormat GetGlFormat() => GLFormat.GL_RGBA;
-
- public GLType GetGlType()
- => GLType.GL_BYTE;
-
- public uint GetGlTypeSize()
- => 1;
-
- public DXGI_FORMAT GetDxgiFormat() => DXGI_FORMAT.DXGI_FORMAT_R8G8B8A8_UNORM;
- }
-}
diff --git a/BCnEnc.Net/Shared/ArrayExtensions.cs b/BCnEnc.Net/Shared/ArrayExtensions.cs
new file mode 100644
index 0000000..41281c7
--- /dev/null
+++ b/BCnEnc.Net/Shared/ArrayExtensions.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BCnEncoder.Shared
+{
+ internal static class ArrayExtensions
+ {
+
+ public static void Fill(this T[] arr, T item)
+ {
+ for (int i = 0; i < arr.Length; i++)
+ {
+ arr[i] = item;
+ }
+ }
+
+ public static void Clear(this T[] arr)
+ {
+ Fill(arr, default);
+ }
+ }
+}
diff --git a/BCnEnc.Net/Shared/Bc7Block.cs b/BCnEnc.Net/Shared/Bc7Block.cs
index c63e1a8..eff404a 100644
--- a/BCnEnc.Net/Shared/Bc7Block.cs
+++ b/BCnEnc.Net/Shared/Bc7Block.cs
@@ -23,9 +23,9 @@ internal struct Bc7Block
public ulong lowBits;
public ulong highBits;
- public static ReadOnlySpan colorInterpolationWeights2 => new byte[] { 0, 21, 43, 64 };
- public static ReadOnlySpan colorInterpolationWeights3 => new byte[] { 0, 9, 18, 27, 37, 46, 55, 64 };
- public static ReadOnlySpan colorInterpolationWeights4 => new byte[] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
+ public static byte[] colorInterpolationWeights2 = new byte[] { 0, 21, 43, 64 };
+ public static byte[] colorInterpolationWeights3 = new byte[] { 0, 9, 18, 27, 37, 46, 55, 64 };
+ public static byte[] colorInterpolationWeights4 = new byte[] { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
public static readonly int[][] Subsets2PartitionTable = {
@@ -212,131 +212,203 @@ public Bc7BlockType Type
}
}
- public int NumSubsets => Type switch
+ public int NumSubsets
{
- Bc7BlockType.Type0 => 3,
- Bc7BlockType.Type1 => 2,
- Bc7BlockType.Type2 => 3,
- Bc7BlockType.Type3 => 2,
- Bc7BlockType.Type7 => 2,
- _ => 1
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return 3;
+ case Bc7BlockType.Type1: return 2;
+ case Bc7BlockType.Type2: return 3;
+ case Bc7BlockType.Type3: return 2;
+ case Bc7BlockType.Type7: return 2;
+ default: return 1;
+ }
+ }
+ }
- public bool HasSubsets => Type switch
+ public bool HasSubsets
{
- Bc7BlockType.Type0 => true,
- Bc7BlockType.Type1 => true,
- Bc7BlockType.Type2 => true,
- Bc7BlockType.Type3 => true,
- Bc7BlockType.Type7 => true,
- _ => false
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return true;
+ case Bc7BlockType.Type1: return true;
+ case Bc7BlockType.Type2: return true;
+ case Bc7BlockType.Type3: return true;
+ case Bc7BlockType.Type7: return true;
+ default: return false;
+ }
+ }
+ }
- public int PartitionSetId => Type switch
+ public int PartitionSetId
{
- Bc7BlockType.Type0 => ByteHelper.Extract4(lowBits, 1),
- Bc7BlockType.Type1 => ByteHelper.Extract6(lowBits, 2),
- Bc7BlockType.Type2 => ByteHelper.Extract6(lowBits, 3),
- Bc7BlockType.Type3 => ByteHelper.Extract6(lowBits, 4),
- Bc7BlockType.Type7 => ByteHelper.Extract6(lowBits, 8),
- _ => -1
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return ByteHelper.Extract4(lowBits, 1);
+ case Bc7BlockType.Type1: return ByteHelper.Extract6(lowBits, 2);
+ case Bc7BlockType.Type2: return ByteHelper.Extract6(lowBits, 3);
+ case Bc7BlockType.Type3: return ByteHelper.Extract6(lowBits, 4);
+ case Bc7BlockType.Type7: return ByteHelper.Extract6(lowBits, 8);
+ default: return -1;
+ }
+ }
+ }
- public byte RotationBits => Type switch
+ public byte RotationBits
{
- Bc7BlockType.Type4 => ByteHelper.Extract2(lowBits, 5),
- Bc7BlockType.Type5 => ByteHelper.Extract2(lowBits, 6),
- _ => 0
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type4: return ByteHelper.Extract2(lowBits, 5);
+ case Bc7BlockType.Type5: return ByteHelper.Extract2(lowBits, 6);
+ default: return 0;
+ }
+ }
+ }
///
/// Bitcount of color component including Pbit
///
- public int ColorComponentPrecision => Type switch
+ public int ColorComponentPrecision
{
- Bc7BlockType.Type0 => 5,
- Bc7BlockType.Type1 => 7,
- Bc7BlockType.Type2 => 5,
- Bc7BlockType.Type3 => 8,
- Bc7BlockType.Type4 => 5,
- Bc7BlockType.Type5 => 7,
- Bc7BlockType.Type6 => 8,
- Bc7BlockType.Type7 => 6,
- _ => 0
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return 5;
+ case Bc7BlockType.Type1: return 7;
+ case Bc7BlockType.Type2: return 5;
+ case Bc7BlockType.Type3: return 8;
+ case Bc7BlockType.Type4: return 5;
+ case Bc7BlockType.Type5: return 7;
+ case Bc7BlockType.Type6: return 8;
+ case Bc7BlockType.Type7: return 6;
+ default: return 0;
+ }
+ }
+ }
///
/// Bitcount of alpha component including Pbit
///
- public int AlphaComponentPrecision => Type switch
+ public int AlphaComponentPrecision
{
+ get
+ {
+ switch (Type)
+ {
- Bc7BlockType.Type4 => 6,
- Bc7BlockType.Type5 => 8,
- Bc7BlockType.Type6 => 8,
- Bc7BlockType.Type7 => 6,
- _ => 0
- };
+ case Bc7BlockType.Type4: return 6;
+ case Bc7BlockType.Type5: return 8;
+ case Bc7BlockType.Type6: return 8;
+ case Bc7BlockType.Type7: return 6;
+ default: return 0;
+ }
+ }
+ }
- public bool HasRotationBits => Type switch
+ public bool HasRotationBits
{
- Bc7BlockType.Type4 => true,
- Bc7BlockType.Type5 => true,
- _ => false
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type4: return true;
+ case Bc7BlockType.Type5: return true;
+ default: return false;
+ }
+ }
+ }
- public bool HasPBits => Type switch
+ public bool HasPBits
{
- Bc7BlockType.Type0 => true,
- Bc7BlockType.Type1 => true,
- Bc7BlockType.Type3 => true,
- Bc7BlockType.Type6 => true,
- Bc7BlockType.Type7 => true,
- _ => false
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return true;
+ case Bc7BlockType.Type1: return true;
+ case Bc7BlockType.Type3: return true;
+ case Bc7BlockType.Type6: return true;
+ case Bc7BlockType.Type7: return true;
+ default: return false;
+ }
+ }
+ }
- public bool HasAlpha => Type switch
+ public bool HasAlpha
{
- Bc7BlockType.Type4 => true,
- Bc7BlockType.Type5 => true,
- Bc7BlockType.Type6 => true,
- Bc7BlockType.Type7 => true,
- _ => false
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type4: return true;
+ case Bc7BlockType.Type5: return true;
+ case Bc7BlockType.Type6: return true;
+ case Bc7BlockType.Type7: return true;
+ default: return false;
+ }
+ }
+ }
///
- /// Type 4 has 2-bit and 3-bit indices. If index mode is 0, color components will use 2-bit indices and alpha will use 3-bit indices.
- /// In index mode 1, color will use 3-bit indices and alpha will use 2-bit indices.
+ /// Type 4 has 2-bit and 3-bit indices. If index mode is 0; color components will use 2-bit indices and alpha will use 3-bit indices.
+ /// In index mode 1; color will use 3-bit indices and alpha will use 2-bit indices.
///
- public int Type4IndexMode => Type switch
+ public int Type4IndexMode
{
- Bc7BlockType.Type4 => ByteHelper.Extract1(lowBits, 7),
- _ => 0
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type4: return ByteHelper.Extract1(lowBits, 7);
+ default: return 0;
+ }
+ }
+ }
- public int ColorIndexBitCount => Type switch
+ public int ColorIndexBitCount
{
- Bc7BlockType.Type0 => 3,
- Bc7BlockType.Type1 => 3,
- Bc7BlockType.Type2 => 2,
- Bc7BlockType.Type3 => 2,
- Bc7BlockType.Type4 when Type4IndexMode == 0 => 2,
- Bc7BlockType.Type4 when Type4IndexMode == 1 => 3,
- Bc7BlockType.Type5 => 2,
- Bc7BlockType.Type6 => 4,
- Bc7BlockType.Type7 => 2,
- _ => 0
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type0: return 3;
+ case Bc7BlockType.Type1: return 3;
+ case Bc7BlockType.Type2: return 2;
+ case Bc7BlockType.Type3: return 2;
+ case Bc7BlockType.Type4 when Type4IndexMode == 0: return 2;
+ case Bc7BlockType.Type4 when Type4IndexMode == 1: return 3;
+ case Bc7BlockType.Type5: return 2;
+ case Bc7BlockType.Type6: return 4;
+ case Bc7BlockType.Type7: return 2;
+ default: return 0;
+ }
+ }
+ }
- public int AlphaIndexBitCount => Type switch
+ public int AlphaIndexBitCount
{
- Bc7BlockType.Type4 when Type4IndexMode == 0 => 3,
- Bc7BlockType.Type4 when Type4IndexMode == 1 => 2,
- Bc7BlockType.Type5 => 2,
- Bc7BlockType.Type6 => 4,
- Bc7BlockType.Type7 => 2,
- _ => 0
- };
+ get
+ {
+ switch (Type)
+ {
+ case Bc7BlockType.Type4 when Type4IndexMode == 0: return 3;
+ case Bc7BlockType.Type4 when Type4IndexMode == 1: return 2;
+ case Bc7BlockType.Type5: return 2;
+ case Bc7BlockType.Type6: return 4;
+ case Bc7BlockType.Type7: return 2;
+ default: return 0;
+ }
+ }
+ }
@@ -523,7 +595,7 @@ private byte[] ExtractPBitArray()
ByteHelper.Extract1(highBits, 97 - 64)
};
default:
- return Array.Empty();
+ return new byte[0];
}
}
@@ -714,7 +786,8 @@ private int GetIndexBegin(Bc7BlockType type, int bitCount, bool IsAlpha)
}
}
- private int GetAlphaIndex(Bc7BlockType type, int numSubsets, int partitionIndex, int bitCount, int index) {
+ private int GetAlphaIndex(Bc7BlockType type, int numSubsets, int partitionIndex, int bitCount, int index)
+ {
if (bitCount == 0) return 0; // No Alpha
int indexOffset = GetIndexOffset(type, numSubsets, partitionIndex, bitCount, index);
int indexBitCount = GetIndexBitCount(numSubsets, partitionIndex, bitCount, index);
@@ -733,19 +806,20 @@ private int GetColorIndex(Bc7BlockType type, int numSubsets, int partitionIndex,
private ColorRgba32 InterpolateColor(ColorRgba32 endPointStart, ColorRgba32 endPointEnd,
int colorIndex, int alphaIndex, int colorBitCount, int alphaBitCount)
{
-
- byte InterpolateByte(byte e0, byte e1, int index, int indexPrecision) {
+
+ byte InterpolateByte(byte e0, byte e1, int index, int indexPrecision)
+ {
if (indexPrecision == 0) return e0;
- ReadOnlySpan aWeights2 = colorInterpolationWeights2;
- ReadOnlySpan aWeights3 = colorInterpolationWeights3;
- ReadOnlySpan aWeights4 = colorInterpolationWeights4;
-
- if(indexPrecision == 2)
- return (byte) (((64 - aWeights2[index])* (e0) + aWeights2[index]*(e1) + 32) >> 6);
- else if(indexPrecision == 3)
- return (byte) (((64 - aWeights3[index])*(e0) + aWeights3[index]*(e1) + 32) >> 6);
+ byte[] aWeights2 = colorInterpolationWeights2;
+ byte[] aWeights3 = colorInterpolationWeights3;
+ byte[] aWeights4 = colorInterpolationWeights4;
+
+ if (indexPrecision == 2)
+ return (byte)(((64 - aWeights2[index]) * (e0) + aWeights2[index] * (e1) + 32) >> 6);
+ else if (indexPrecision == 3)
+ return (byte)(((64 - aWeights3[index]) * (e0) + aWeights3[index] * (e1) + 32) >> 6);
else // indexprecision == 4
- return (byte) (((64 - aWeights4[index])*(e0) + aWeights4[index]*(e1) + 32) >> 6);
+ return (byte)(((64 - aWeights4[index]) * (e0) + aWeights4[index] * (e1) + 32) >> 6);
}
ColorRgba32 result = new ColorRgba32(
@@ -763,8 +837,10 @@ byte InterpolateByte(byte e0, byte e1, int index, int indexPrecision) {
/// 10 – swap A and G
/// 11 - swap A and B
///
- private static ColorRgba32 SwapChannels(ColorRgba32 source, int rotation) {
- switch (rotation) {
+ private static ColorRgba32 SwapChannels(ColorRgba32 source, int rotation)
+ {
+ switch (rotation)
+ {
case 0b00:
return source;
case 0b01:
@@ -796,13 +872,11 @@ public RawBlock4X4Rgba32 Decode()
//subset_index = get_partition_index(num_subsets, partition_set_id, x, y);
}
- var pixels = output.AsSpan;
-
bool hasRotationBits = HasRotationBits;
int rotation = RotationBits;
ColorRgba32[] endpoints = ExtractEndpoints();
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
int subsetIndex = GetPartitionIndex(numSubsets, partitionIndex, i);
@@ -817,7 +891,8 @@ public RawBlock4X4Rgba32 Decode()
ColorRgba32 outputColor = InterpolateColor(endPointStart, endPointEnd, colorIndex, alphaIndex,
colorBitCount, alphaBitCount);
- if (hasRotationBits) {
+ if (hasRotationBits)
+ {
//Decode the 2 color rotation bits as follows:
// 00 – Block format is Scalar(A) Vector(RGB) - no swapping
// 01 – Block format is Scalar(R) Vector(AGB) - swap A and R
@@ -826,115 +901,128 @@ public RawBlock4X4Rgba32 Decode()
outputColor = SwapChannels(outputColor, rotation);
}
- pixels[i] = outputColor.ToRgba32();
+ output[i] = outputColor.ToRgba32();
}
return output;
}
- public void PackType0(int partitionIndex4Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices) {
+ public void PackType0(int partitionIndex4Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices)
+ {
Debug.Assert(partitionIndex4Bit < 16, "Mode 0 should have 4bit partition index");
Debug.Assert(subsetEndpoints.Length == 6, "Mode 0 should have 6 endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.Length == 3) , "Mode 0 should have RGB endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 4))) , "Mode 0 should have 4bit endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.Length == 3), "Mode 0 should have RGB endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 4))), "Mode 0 should have 4bit endpoints");
Debug.Assert(pBits.Length == 6, "Mode 0 should have 6 pBits");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 3)) , "Mode 0 should have 3bit indices");
+ Debug.Assert(indices.All(x => x < (1 << 3)), "Mode 0 should have 3bit indices");
lowBits = 1; // Set Mode 0
highBits = 0;
- lowBits = ByteHelper.Store4(lowBits, 1, (byte) partitionIndex4Bit);
+ lowBits = ByteHelper.Store4(lowBits, 1, (byte)partitionIndex4Bit);
int nextIdx = 5;
//Store endpoints
- for (int i = 0; i < subsetEndpoints[0].Length; i++) {
- for (int j = 0; j < subsetEndpoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx, 4, subsetEndpoints[j][i]);
+ for (int i = 0; i < subsetEndpoints[0].Length; i++)
+ {
+ for (int j = 0; j < subsetEndpoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx, 4, subsetEndpoints[j][i]);
nextIdx += 4;
}
}
//Store pBits
- for (int i = 0; i < pBits.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx, 1, pBits[i]);
+ for (int i = 0; i < pBits.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx, 1, pBits[i]);
nextIdx++;
}
Debug.Assert(nextIdx == 83);
int colorBitCount = ColorIndexBitCount;
int indexBegin = GetIndexBegin(Bc7BlockType.Type0, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type0, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type0, NumSubsets,
partitionIndex4Bit, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
partitionIndex4Bit, colorBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
indexBegin + indexOffset, indexBitCount, indices[i]);
}
}
- public void PackType1(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices) {
+ public void PackType1(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices)
+ {
Debug.Assert(partitionIndex6Bit < 64, "Mode 1 should have 6bit partition index");
Debug.Assert(subsetEndpoints.Length == 4, "Mode 1 should have 4 endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.Length == 3) , "Mode 1 should have RGB endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 6))) , "Mode 1 should have 6bit endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.Length == 3), "Mode 1 should have RGB endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 6))), "Mode 1 should have 6bit endpoints");
Debug.Assert(pBits.Length == 2, "Mode 1 should have 2 pBits");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 3)) , "Mode 1 should have 3bit indices");
+ Debug.Assert(indices.All(x => x < (1 << 3)), "Mode 1 should have 3bit indices");
lowBits = 2; // Set Mode 1
highBits = 0;
- lowBits = ByteHelper.Store6(lowBits, 2, (byte) partitionIndex6Bit);
+ lowBits = ByteHelper.Store6(lowBits, 2, (byte)partitionIndex6Bit);
int nextIdx = 8;
//Store endpoints
- for (int i = 0; i < subsetEndpoints[0].Length; i++) {
- for (int j = 0; j < subsetEndpoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx, 6, subsetEndpoints[j][i]);
+ for (int i = 0; i < subsetEndpoints[0].Length; i++)
+ {
+ for (int j = 0; j < subsetEndpoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx, 6, subsetEndpoints[j][i]);
nextIdx += 6;
}
}
//Store pBits
- for (int i = 0; i < pBits.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx, 1, pBits[i]);
+ for (int i = 0; i < pBits.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx, 1, pBits[i]);
nextIdx++;
}
Debug.Assert(nextIdx == 82);
int colorBitCount = ColorIndexBitCount;
int indexBegin = GetIndexBegin(Bc7BlockType.Type1, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type1, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type1, NumSubsets,
partitionIndex6Bit, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
partitionIndex6Bit, colorBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
indexBegin + indexOffset, indexBitCount, indices[i]);
}
}
- public void PackType2(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] indices) {
+ public void PackType2(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] indices)
+ {
Debug.Assert(partitionIndex6Bit < 64, "Mode 2 should have 6bit partition index");
Debug.Assert(subsetEndpoints.Length == 6, "Mode 2 should have 6 endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.Length == 3) , "Mode 2 should have RGB endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 5))) , "Mode 2 should have 5bit endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.Length == 3), "Mode 2 should have RGB endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 5))), "Mode 2 should have 5bit endpoints");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 2)) , "Mode 2 should have 2bit indices");
+ Debug.Assert(indices.All(x => x < (1 << 2)), "Mode 2 should have 2bit indices");
lowBits = 0b100; // Set Mode 2
highBits = 0;
- lowBits = ByteHelper.Store6(lowBits, 3, (byte) partitionIndex6Bit);
+ lowBits = ByteHelper.Store6(lowBits, 3, (byte)partitionIndex6Bit);
int nextIdx = 9;
//Store endpoints
- for (int i = 0; i < subsetEndpoints[0].Length; i++) {
- for (int j = 0; j < subsetEndpoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < subsetEndpoints[0].Length; i++)
+ {
+ for (int j = 0; j < subsetEndpoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 5, subsetEndpoints[j][i]);
nextIdx += 5;
}
@@ -943,43 +1031,48 @@ public void PackType2(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[]
int colorBitCount = ColorIndexBitCount;
int indexBegin = GetIndexBegin(Bc7BlockType.Type2, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type2, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type2, NumSubsets,
partitionIndex6Bit, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
partitionIndex6Bit, colorBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
indexBegin + indexOffset, indexBitCount, indices[i]);
}
}
- public void PackType3(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices) {
+ public void PackType3(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices)
+ {
Debug.Assert(partitionIndex6Bit < 64, "Mode 3 should have 6bit partition index");
Debug.Assert(subsetEndpoints.Length == 4, "Mode 3 should have 4 endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.Length == 3) , "Mode 3 should have RGB endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 7))) , "Mode 3 should have 7bit endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.Length == 3), "Mode 3 should have RGB endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 7))), "Mode 3 should have 7bit endpoints");
Debug.Assert(pBits.Length == 4, "Mode 3 should have 4 pBits");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 2)) , "Mode 3 should have 2bit indices");
+ Debug.Assert(indices.All(x => x < (1 << 2)), "Mode 3 should have 2bit indices");
lowBits = 0b1000; // Set Mode 3
highBits = 0;
- lowBits = ByteHelper.Store6(lowBits, 4, (byte) partitionIndex6Bit);
+ lowBits = ByteHelper.Store6(lowBits, 4, (byte)partitionIndex6Bit);
int nextIdx = 10;
//Store endpoints
- for (int i = 0; i < subsetEndpoints[0].Length; i++) {
- for (int j = 0; j < subsetEndpoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < subsetEndpoints[0].Length; i++)
+ {
+ for (int j = 0; j < subsetEndpoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 7, subsetEndpoints[j][i]);
nextIdx += 7;
}
}
//Store pBits
- for (int i = 0; i < pBits.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx,
+ for (int i = 0; i < pBits.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx,
1, pBits[i]);
nextIdx++;
}
@@ -987,179 +1080,200 @@ public void PackType3(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] p
int colorBitCount = ColorIndexBitCount;
int indexBegin = GetIndexBegin(Bc7BlockType.Type3, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type3, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type3, NumSubsets,
partitionIndex6Bit, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
partitionIndex6Bit, colorBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
indexBegin + indexOffset, indexBitCount, indices[i]);
}
}
- public void PackType4(int rotation, byte idxMode, byte[][] colorEndPoints, byte[] alphaEndPoints, byte[] indices2Bit, byte[] indices3Bit) {
+ public void PackType4(int rotation, byte idxMode, byte[][] colorEndPoints, byte[] alphaEndPoints, byte[] indices2Bit, byte[] indices3Bit)
+ {
Debug.Assert(rotation < 4, "Rotation can only be 0-3");
Debug.Assert(idxMode < 2, "IndexMode can only be 0 or 1");
Debug.Assert(colorEndPoints.Length == 2, "Mode 4 should have 2 endpoints");
- Debug.Assert(colorEndPoints.All(x => x.Length == 3) , "Mode 4 should have RGB color endpoints");
- Debug.Assert(colorEndPoints.All(x => x.All(y => y < (1 << 5))) , "Mode 4 should have 5bit color endpoints");
+ Debug.Assert(colorEndPoints.All(x => x.Length == 3), "Mode 4 should have RGB color endpoints");
+ Debug.Assert(colorEndPoints.All(x => x.All(y => y < (1 << 5))), "Mode 4 should have 5bit color endpoints");
Debug.Assert(alphaEndPoints.Length == 2, "Mode 4 should have 2 endpoints");
- Debug.Assert(alphaEndPoints.All(x => x < (1 << 6)) , "Mode 4 should have 6bit alpha endpoints");
+ Debug.Assert(alphaEndPoints.All(x => x < (1 << 6)), "Mode 4 should have 6bit alpha endpoints");
Debug.Assert(indices2Bit.Length == 16, "Provide 16 indices");
- Debug.Assert(indices2Bit.All(x => x < (1 << 2)) , "Mode 4 should have 2bit indices");
+ Debug.Assert(indices2Bit.All(x => x < (1 << 2)), "Mode 4 should have 2bit indices");
Debug.Assert(indices3Bit.Length == 16, "Provide 16 indices");
- Debug.Assert(indices3Bit.All(x => x < (1 << 3)) , "Mode 4 should have 3bit indices");
+ Debug.Assert(indices3Bit.All(x => x < (1 << 3)), "Mode 4 should have 3bit indices");
lowBits = 0b10000; // Set Mode 4
highBits = 0;
- lowBits = ByteHelper.Store2(lowBits, 5, (byte) rotation);
- lowBits = ByteHelper.Store1(lowBits, 7, (byte) idxMode);
+ lowBits = ByteHelper.Store2(lowBits, 5, (byte)rotation);
+ lowBits = ByteHelper.Store1(lowBits, 7, (byte)idxMode);
int nextIdx = 8;
//Store color endpoints
- for (int i = 0; i < colorEndPoints[0].Length; i++) {
- for (int j = 0; j < colorEndPoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < colorEndPoints[0].Length; i++)
+ {
+ for (int j = 0; j < colorEndPoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 5, colorEndPoints[j][i]);
nextIdx += 5;
}
}
//Store alpha endpoints
- for (int i = 0; i < alphaEndPoints.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx,
+ for (int i = 0; i < alphaEndPoints.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx,
6, alphaEndPoints[i]);
- nextIdx+=6;
+ nextIdx += 6;
}
Debug.Assert(nextIdx == 50);
int colorBitCount = ColorIndexBitCount;
int colorIndexBegin = GetIndexBegin(Bc7BlockType.Type4, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type4, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type4, NumSubsets,
0, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
0, colorBitCount, i);
- if (idxMode == 0) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ if (idxMode == 0)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
colorIndexBegin + indexOffset, indexBitCount, indices2Bit[i]);
}
- else {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ else
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
colorIndexBegin + indexOffset, indexBitCount, indices3Bit[i]);
}
}
int alphaBitCount = AlphaIndexBitCount;
int alphaIndexBegin = GetIndexBegin(Bc7BlockType.Type4, alphaBitCount, true);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type4, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type4, NumSubsets,
0, alphaBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
0, alphaBitCount, i);
- if (idxMode == 0) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ if (idxMode == 0)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
alphaIndexBegin + indexOffset, indexBitCount, indices3Bit[i]);
}
- else {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ else
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
alphaIndexBegin + indexOffset, indexBitCount, indices2Bit[i]);
}
}
}
- public void PackType5(int rotation, byte[][] colorEndPoints, byte[] alphaEndPoints, byte[] colorIndices, byte[] alphaIndices) {
+ public void PackType5(int rotation, byte[][] colorEndPoints, byte[] alphaEndPoints, byte[] colorIndices, byte[] alphaIndices)
+ {
Debug.Assert(rotation < 4, "Rotation can only be 0-3");
Debug.Assert(colorEndPoints.Length == 2, "Mode 5 should have 2 endpoints");
- Debug.Assert(colorEndPoints.All(x => x.Length == 3) , "Mode 5 should have RGB color endpoints");
- Debug.Assert(colorEndPoints.All(x => x.All(y => y < (1 << 7))) , "Mode 5 should have 7bit color endpoints");
+ Debug.Assert(colorEndPoints.All(x => x.Length == 3), "Mode 5 should have RGB color endpoints");
+ Debug.Assert(colorEndPoints.All(x => x.All(y => y < (1 << 7))), "Mode 5 should have 7bit color endpoints");
Debug.Assert(alphaEndPoints.Length == 2, "Mode 5 should have 2 endpoints");
Debug.Assert(colorIndices.Length == 16, "Provide 16 indices");
- Debug.Assert(colorIndices.All(x => x < (1 << 2)) , "Mode 5 should have 2bit color indices");
+ Debug.Assert(colorIndices.All(x => x < (1 << 2)), "Mode 5 should have 2bit color indices");
Debug.Assert(alphaIndices.Length == 16, "Provide 16 indices");
- Debug.Assert(alphaIndices.All(x => x < (1 << 2)) , "Mode 5 should have 2bit alpha indices");
+ Debug.Assert(alphaIndices.All(x => x < (1 << 2)), "Mode 5 should have 2bit alpha indices");
lowBits = 0b100000; // Set Mode 5
highBits = 0;
- lowBits = ByteHelper.Store2(lowBits, 6, (byte) rotation);
+ lowBits = ByteHelper.Store2(lowBits, 6, (byte)rotation);
int nextIdx = 8;
//Store color endpoints
- for (int i = 0; i < colorEndPoints[0].Length; i++) {
- for (int j = 0; j < colorEndPoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < colorEndPoints[0].Length; i++)
+ {
+ for (int j = 0; j < colorEndPoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 7, colorEndPoints[j][i]);
nextIdx += 7;
}
}
//Store alpha endpoints
- for (int i = 0; i < alphaEndPoints.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx,
+ for (int i = 0; i < alphaEndPoints.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx,
8, alphaEndPoints[i]);
- nextIdx+=8;
+ nextIdx += 8;
}
Debug.Assert(nextIdx == 66);
int colorBitCount = ColorIndexBitCount;
int colorIndexBegin = GetIndexBegin(Bc7BlockType.Type5, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type5, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type5, NumSubsets,
0, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
0, colorBitCount, i);
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
- colorIndexBegin + indexOffset, indexBitCount, colorIndices[i]);
-
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
+ colorIndexBegin + indexOffset, indexBitCount, colorIndices[i]);
+
}
int alphaBitCount = AlphaIndexBitCount;
int alphaIndexBegin = GetIndexBegin(Bc7BlockType.Type5, alphaBitCount, true);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type5, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type5, NumSubsets,
0, alphaBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
0, alphaBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
alphaIndexBegin + indexOffset, indexBitCount, alphaIndices[i]);
}
}
- public void PackType6(byte[][] colorAlphaEndPoints, byte[] pBits, byte[] indices) {
- Debug.Assert(colorAlphaEndPoints.Length == 2,
+ public void PackType6(byte[][] colorAlphaEndPoints, byte[] pBits, byte[] indices)
+ {
+ Debug.Assert(colorAlphaEndPoints.Length == 2,
"Mode 6 should have 2 endpoints");
- Debug.Assert(colorAlphaEndPoints.All(x => x.Length == 4) ,
+ Debug.Assert(colorAlphaEndPoints.All(x => x.Length == 4),
"Mode 6 should have RGBA color endpoints");
- Debug.Assert(colorAlphaEndPoints.All(x => x.All(y => y < (1 << 7))) ,
+ Debug.Assert(colorAlphaEndPoints.All(x => x.All(y => y < (1 << 7))),
"Mode 6 should have 7bit color and alpha endpoints");
Debug.Assert(pBits.Length == 2, "Mode 6 should have 2 pBits");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 4)) , "Mode 6 should have 4bit color indices");
-
+ Debug.Assert(indices.All(x => x < (1 << 4)), "Mode 6 should have 4bit color indices");
+
lowBits = 0b1000000; // Set Mode 6
highBits = 0;
int nextIdx = 7;
//Store color and alpha endpoints
- for (int i = 0; i < colorAlphaEndPoints[0].Length; i++) {
- for (int j = 0; j < colorAlphaEndPoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < colorAlphaEndPoints[0].Length; i++)
+ {
+ for (int j = 0; j < colorAlphaEndPoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 7, colorAlphaEndPoints[j][i]);
nextIdx += 7;
}
}
//Store pBits
- for (int i = 0; i < pBits.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx,
+ for (int i = 0; i < pBits.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx,
1, pBits[i]);
nextIdx++;
}
@@ -1167,44 +1281,49 @@ public void PackType6(byte[][] colorAlphaEndPoints, byte[] pBits, byte[] indices
int colorBitCount = ColorIndexBitCount;
int colorIndexBegin = GetIndexBegin(Bc7BlockType.Type6, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type6, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type6, NumSubsets,
0, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
0, colorBitCount, i);
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
- colorIndexBegin + indexOffset, indexBitCount, indices[i]);
-
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
+ colorIndexBegin + indexOffset, indexBitCount, indices[i]);
+
}
}
- public void PackType7(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices) {
+ public void PackType7(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] pBits, byte[] indices)
+ {
Debug.Assert(partitionIndex6Bit < 64, "Mode 7 should have 6bit partition index");
Debug.Assert(subsetEndpoints.Length == 4, "Mode 7 should have 4 endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.Length == 4) , "Mode 7 should have RGBA endpoints");
- Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 5))) , "Mode 7 should have 5bit endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.Length == 4), "Mode 7 should have RGBA endpoints");
+ Debug.Assert(subsetEndpoints.All(x => x.All(y => y < (1 << 5))), "Mode 7 should have 5bit endpoints");
Debug.Assert(pBits.Length == 4, "Mode 7 should have 4 pBits");
Debug.Assert(indices.Length == 16, "Provide 16 indices");
- Debug.Assert(indices.All(x => x < (1 << 2)) , "Mode 3 should have 2bit indices");
+ Debug.Assert(indices.All(x => x < (1 << 2)), "Mode 3 should have 2bit indices");
lowBits = 0b10000000; // Set Mode 7
highBits = 0;
- lowBits = ByteHelper.Store6(lowBits, 8, (byte) partitionIndex6Bit);
+ lowBits = ByteHelper.Store6(lowBits, 8, (byte)partitionIndex6Bit);
int nextIdx = 14;
//Store endpoints
- for (int i = 0; i < subsetEndpoints[0].Length; i++) {
- for (int j = 0; j < subsetEndpoints.Length; j++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+ for (int i = 0; i < subsetEndpoints[0].Length; i++)
+ {
+ for (int j = 0; j < subsetEndpoints.Length; j++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
nextIdx, 5, subsetEndpoints[j][i]);
nextIdx += 5;
}
}
//Store pBits
- for (int i = 0; i < pBits.Length; i++) {
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits, nextIdx,
+ for (int i = 0; i < pBits.Length; i++)
+ {
+ ByteHelper.StoreTo128(ref lowBits, ref highBits, nextIdx,
1, pBits[i]);
nextIdx++;
}
@@ -1212,13 +1331,14 @@ public void PackType7(int partitionIndex6Bit, byte[][] subsetEndpoints, byte[] p
int colorBitCount = ColorIndexBitCount;
int indexBegin = GetIndexBegin(Bc7BlockType.Type7, colorBitCount, false);
- for (int i = 0; i < 16; i++) {
- int indexOffset = GetIndexOffset(Bc7BlockType.Type7, NumSubsets,
+ for (int i = 0; i < 16; i++)
+ {
+ int indexOffset = GetIndexOffset(Bc7BlockType.Type7, NumSubsets,
partitionIndex6Bit, colorBitCount, i);
- int indexBitCount = GetIndexBitCount(NumSubsets,
+ int indexBitCount = GetIndexBitCount(NumSubsets,
partitionIndex6Bit, colorBitCount, i);
-
- (lowBits, highBits) = ByteHelper.StoreTo128(lowBits, highBits,
+
+ ByteHelper.StoreTo128(ref lowBits, ref highBits,
indexBegin + indexOffset, indexBitCount, indices[i]);
}
}
diff --git a/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs b/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs
index ebdf410..0e5817c 100644
--- a/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs
+++ b/BCnEnc.Net/Shared/BinaryReaderWriterExtensions.cs
@@ -9,20 +9,42 @@ internal static class BinaryReaderWriterExtensions
{
public static unsafe void WriteStruct(this BinaryWriter bw, T t) where T : unmanaged
{
- int size = Unsafe.SizeOf();
+ int size = sizeof(T);
byte* bytes = stackalloc byte[size];
- Unsafe.Write(bytes, t);
- Span bSpan = new Span(bytes, size);
- bw.Write(bSpan);
+ ((T*) bytes)[0] = t;
+ for (int b = 0; b < size; b++)
+ {
+ bw.Write(bytes[b]);
+ }
}
public static unsafe T ReadStruct(this BinaryReader br) where T : unmanaged
{
- int size = Unsafe.SizeOf();
+ int size = sizeof(T);
byte* bytes = stackalloc byte[size];
- Span bSpan = new Span(bytes, size);
- br.Read(bSpan);
- return Unsafe.Read(bytes);
+ for (int b = 0; b < size; b++)
+ {
+ bytes[b] = br.ReadByte();
+ }
+ return ((T*)bytes)[0];
+ }
+
+ public static void Read(this BinaryReader br, byte[] data)
+ {
+ for (int b = 0; b < data.Length; b++)
+ {
+ data[b] = br.ReadByte();
+ }
+ }
+
+ public static byte[] Slice(this byte[] bytes, int start, int length)
+ {
+ byte[] output = new byte[length];
+ for (int i = 0; i < length; i++)
+ {
+ output[i] = bytes[i + start];
+ }
+ return output;
}
public static void AddPadding(this BinaryWriter bw, uint padding)
diff --git a/BCnEnc.Net/Shared/ByteHelper.cs b/BCnEnc.Net/Shared/ByteHelper.cs
index 4359cf2..f52e20d 100644
--- a/BCnEnc.Net/Shared/ByteHelper.cs
+++ b/BCnEnc.Net/Shared/ByteHelper.cs
@@ -168,15 +168,15 @@ public static ulong ExtractFrom128(ulong low, ulong high, int index, int bitCoun
}
}
- public static (ulong, ulong) StoreTo128(ulong low, ulong high, int index, int bitCount, ulong value)
+ public static void StoreTo128(ref ulong low, ref ulong high, int index, int bitCount, ulong value)
{
if (index + bitCount <= 64)
{ // Store to low
- return (Store(low, index, bitCount, value), high);
+ low = Store(low, index, bitCount, value);
}
else if (index >= 64)
{ // Store to high
- return (low, Store(high, index - 64, bitCount, value));
+ high = Store(high, index - 64, bitCount, value);
}
else
{ //handle boundary case
@@ -188,7 +188,8 @@ public static (ulong, ulong) StoreTo128(ulong low, ulong high, int index, int bi
var l = Store(low, lowIndex, lowBitCount, value);
value >>= lowBitCount;
var h = Store(high, highIndex, highBitCount, value);
- return (l, h);
+ low = l;
+ high = h;
}
}
}
diff --git a/BCnEnc.Net/Shared/Colors.cs b/BCnEnc.Net/Shared/Colors.cs
index 620cff7..e923991 100644
--- a/BCnEnc.Net/Shared/Colors.cs
+++ b/BCnEnc.Net/Shared/Colors.cs
@@ -1,6 +1,5 @@
using System;
using System.Numerics;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
@@ -40,7 +39,7 @@ public override int GetHashCode()
public ushort data;
public byte R {
- readonly get {
+ get {
int r5 = ((data & RedMask) >> RedShift);
return (byte)((r5 << 3) | (r5 >> 2));
}
@@ -52,7 +51,7 @@ readonly get {
}
public byte G {
- readonly get {
+ get {
int g6 = ((data & GreenMask) >> GreenShift);
return (byte)((g6 << 2) | (g6 >> 4));
}
@@ -64,7 +63,7 @@ readonly get {
}
public byte B {
- readonly get {
+ get {
int b5 = (data & BlueMask);
return (byte)((b5 << 3) | (b5 >> 2));
}
@@ -76,7 +75,7 @@ readonly get {
}
public int RawR {
- readonly get { return ((data & RedMask) >> RedShift); }
+ get { return ((data & RedMask) >> RedShift); }
set {
if (value > 31) value = 31;
if (value < 0) value = 0;
@@ -86,7 +85,7 @@ public int RawR {
}
public int RawG {
- readonly get { return ((data & GreenMask) >> GreenShift); }
+ get { return ((data & GreenMask) >> GreenShift); }
set {
if (value > 63) value = 63;
if (value < 0) value = 0;
@@ -96,7 +95,7 @@ public int RawG {
}
public int RawB {
- readonly get { return (data & BlueMask); }
+ get { return (data & BlueMask); }
set {
if (value > 31) value = 31;
if (value < 0) value = 0;
@@ -127,7 +126,7 @@ public ColorRgb565(ColorRgb24 color) {
B = color.b;
}
- public readonly ColorRgb24 ToColorRgb24()
+ public ColorRgb24 ToColorRgb24()
{
return new ColorRgb24(R, G, B);
}
@@ -503,7 +502,7 @@ public float CalcDistWeighted(ColorYCbCr other, float yWeight = 4) {
float dcb = (cb - other.cb) * (cb - other.cb);
float dcr = (cr - other.cr) * (cr - other.cr);
- return MathF.Sqrt(dy + dcb + dcr);
+ return (float)Math.Sqrt(dy + dcb + dcr);
}
public static ColorYCbCr operator+(ColorYCbCr left, ColorYCbCr right)
@@ -617,7 +616,7 @@ public float CalcDistWeighted(ColorYCbCrAlpha other, float yWeight = 4, float aW
float dcr = (cr - other.cr) * (cr - other.cr);
float da = (alpha - other.alpha) * (alpha - other.alpha) * aWeight;
- return MathF.Sqrt(dy + dcb + dcr + da);
+ return (float)Math.Sqrt(dy + dcb + dcr + da);
}
public static ColorYCbCrAlpha operator+(ColorYCbCrAlpha left, ColorYCbCrAlpha right)
@@ -672,7 +671,7 @@ public static ColorXyz ColorToXyz(ColorRgb24 color) {
}
private static float PivotRgb(float n) {
- return (n > 0.04045f ? MathF.Pow((n + 0.055f) / 1.055f, 2.4f) : n / 12.92f) * 100;
+ return (n > 0.04045f ? (float)Math.Pow((n + 0.055f) / 1.055f, 2.4f) : n / 12.92f) * 100;
}
}
@@ -719,7 +718,7 @@ public static ColorLab XyzToLab(ColorXyz xyz) {
}
private static float PivotXyz(float n) {
- float i = MathF.Cbrt(n);
+ float i = (float)Math.Pow(n, (1.0 / 3.0));
return n > 0.008856f ? i : 7.787f * n + 16 / 116f;
}
}
diff --git a/BCnEnc.Net/Shared/CompressionFormat.cs b/BCnEnc.Net/Shared/CompressionFormat.cs
index 04bf275..25f8d91 100644
--- a/BCnEnc.Net/Shared/CompressionFormat.cs
+++ b/BCnEnc.Net/Shared/CompressionFormat.cs
@@ -2,22 +2,6 @@
{
public enum CompressionFormat
{
- ///
- /// Raw unsigned byte 8-bit Luminance data
- ///
- R,
- ///
- /// Raw unsigned byte 16-bit RG data
- ///
- RG,
- ///
- /// Raw unsigned byte 24-bit RGB data
- ///
- RGB,
- ///
- /// Raw unsigned byte 32-bit RGBA data
- ///
- RGBA,
///
/// BC1 / DXT1 with no alpha. Very widely supported and good compression ratio.
///
@@ -55,16 +39,7 @@ public enum CompressionFormat
public static class CompressionFormatExtensions {
public static bool IsCompressedFormat(this CompressionFormat format)
{
- switch (format) {
- case CompressionFormat.R:
- case CompressionFormat.RG:
- case CompressionFormat.RGB:
- case CompressionFormat.RGBA:
- return false;
-
- default:
- return true;
- }
+ return true;
}
}
}
diff --git a/BCnEnc.Net/Shared/DdsFile.cs b/BCnEnc.Net/Shared/DdsFile.cs
index 229c529..871f061 100644
--- a/BCnEnc.Net/Shared/DdsFile.cs
+++ b/BCnEnc.Net/Shared/DdsFile.cs
@@ -126,7 +126,10 @@ public static DdsFile Load(Stream s)
}
}
byte[] data = new byte[sizeInBytes];
- br.Read(data);
+ for (int b = 0; b < data.Length; b++)
+ {
+ data[b] = br.ReadByte();
+ }
output.Faces[face].MipMaps[mip] = new DdsMipMap(data, mipWidth, mipHeight);
}
}
@@ -218,7 +221,7 @@ public unsafe struct DdsHeader
public uint dwCaps4;
public uint dwReserved2;
- public static (DdsHeader, DdsHeaderDxt10) InitializeCompressed(int width, int height, DXGI_FORMAT format)
+ public static DdsHeader InitializeCompressed(int width, int height, DXGI_FORMAT format, out DdsHeaderDxt10 dxt10)
{
DdsHeader header = new DdsHeader();
DdsHeaderDxt10 dxt10Header = new DdsHeaderDxt10();
@@ -271,7 +274,8 @@ public static (DdsHeader, DdsHeaderDxt10) InitializeCompressed(int width, int he
dxt10Header.resourceDimension = D3D10_RESOURCE_DIMENSION.D3D10_RESOURCE_DIMENSION_TEXTURE2D;
}
- return (header, dxt10Header);
+ dxt10 = dxt10Header;
+ return header;
}
public static DdsHeader InitializeUncompressed(int width, int height, DXGI_FORMAT format)
diff --git a/BCnEnc.Net/Shared/EncodedBlocks.cs b/BCnEnc.Net/Shared/EncodedBlocks.cs
index bf41884..bbb4d40 100644
--- a/BCnEnc.Net/Shared/EncodedBlocks.cs
+++ b/BCnEnc.Net/Shared/EncodedBlocks.cs
@@ -1,11 +1,10 @@
using System;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
[StructLayout(LayoutKind.Sequential)]
- internal unsafe struct Bc1Block
+ internal struct Bc1Block
{
public ColorRgb565 color0;
public ColorRgb565 color1;
@@ -13,7 +12,7 @@ internal unsafe struct Bc1Block
public int this[int index]
{
- readonly get => (int)(colorIndices >> (index * 2)) & 0b11;
+ get => (int)(colorIndices >> (index * 2)) & 0b11;
set
{
colorIndices = (uint)(colorIndices & (~(0b11 << (index * 2))));
@@ -22,42 +21,41 @@ public int this[int index]
}
}
- public readonly bool HasAlphaOrBlack => color0.data <= color1.data;
+ public bool HasAlphaOrBlack => color0.data <= color1.data;
- public readonly RawBlock4X4Rgba32 Decode(bool useAlpha)
+ public RawBlock4X4Rgba32 Decode(bool useAlpha)
{
RawBlock4X4Rgba32 output = new RawBlock4X4Rgba32();
- var pixels = output.AsSpan;
var color0 = this.color0.ToColorRgb24();
var color1 = this.color1.ToColorRgb24();
useAlpha = useAlpha && HasAlphaOrBlack;
- Span colors = HasAlphaOrBlack ?
- stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = HasAlphaOrBlack ?
+ new ColorRgb24[] {
color0,
color1,
color0 * (1.0 / 2.0) + color1 * (1.0 / 2.0),
new ColorRgb24(0, 0, 0)
- } : stackalloc ColorRgb24[] {
+ } : new ColorRgb24[] {
color0,
color1,
color0 * (2.0 / 3.0) + color1 * (1.0 / 3.0),
color0 * (1.0 / 3.0) + color1 * (2.0 / 3.0)
};
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
int colorIndex = (int)((colorIndices >> (i * 2)) & 0b11);
var color = colors[colorIndex];
if (useAlpha && colorIndex == 3)
{
- pixels[i] = new Rgba32(0, 0, 0, 0);
+ output[i] = new Rgba32(0, 0, 0, 0);
}
else
{
- pixels[i] = new Rgba32(color.r, color.g, color.b, 255);
+ output[i] = new Rgba32(color.r, color.g, color.b, 255);
}
}
return output;
@@ -75,7 +73,7 @@ internal unsafe struct Bc2Block
public int this[int index]
{
- readonly get => (int)(colorIndices >> (index * 2)) & 0b11;
+ get => (int)(colorIndices >> (index * 2)) & 0b11;
set
{
colorIndices = (uint)(colorIndices & (~(0b11 << (index * 2))));
@@ -84,7 +82,7 @@ public int this[int index]
}
}
- public readonly byte GetAlpha(int index)
+ public byte GetAlpha(int index)
{
ulong mask = 0xFUL << (index * 4);
int shift = (index * 4);
@@ -101,27 +99,26 @@ public void SetAlpha(int index, byte alpha)
alphaColors |= (ulong)(a & 0xF) << shift;
}
- public readonly RawBlock4X4Rgba32 Decode()
+ public RawBlock4X4Rgba32 Decode()
{
RawBlock4X4Rgba32 output = new RawBlock4X4Rgba32();
- var pixels = output.AsSpan;
var color0 = this.color0.ToColorRgb24();
var color1 = this.color1.ToColorRgb24();
- Span colors = stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = new ColorRgb24[] {
color0,
color1,
color0 * (2.0 / 3.0) + color1 * (1.0 / 3.0),
color0 * (1.0 / 3.0) + color1 * (2.0 / 3.0)
};
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
int colorIndex = (int)((colorIndices >> (i * 2)) & 0b11);
var color = colors[colorIndex];
- pixels[i] = new Rgba32(color.r, color.g, color.b, GetAlpha(i));
+ output[i] = new Rgba32(color.r, color.g, color.b, GetAlpha(i));
}
return output;
}
@@ -137,7 +134,7 @@ internal unsafe struct Bc3Block
public int this[int index]
{
- readonly get => (int)(colorIndices >> (index * 2)) & 0b11;
+ get => (int)(colorIndices >> (index * 2)) & 0b11;
set
{
colorIndices = (uint)(colorIndices & (~(0b11 << (index * 2))));
@@ -148,7 +145,7 @@ public int this[int index]
public byte Alpha0
{
- readonly get => (byte)(alphaBlock & 0xFFUL);
+ get => (byte)(alphaBlock & 0xFFUL);
set
{
alphaBlock &= ~0xFFUL;
@@ -158,7 +155,7 @@ public byte Alpha0
public byte Alpha1
{
- readonly get => (byte)((alphaBlock >> 8) & 0xFFUL);
+ get => (byte)((alphaBlock >> 8) & 0xFFUL);
set
{
alphaBlock &= ~0xFF00UL;
@@ -166,7 +163,7 @@ public byte Alpha1
}
}
- public readonly byte GetAlphaIndex(int pixelIndex)
+ public byte GetAlphaIndex(int pixelIndex)
{
ulong mask = 0b0111UL << (pixelIndex * 3 + 16);
int shift = (pixelIndex * 3 + 16);
@@ -182,24 +179,23 @@ public void SetAlphaIndex(int pixelIndex, byte alphaIndex)
alphaBlock |= (ulong)(alphaIndex & 0b111) << shift;
}
- public readonly RawBlock4X4Rgba32 Decode()
+ public RawBlock4X4Rgba32 Decode()
{
RawBlock4X4Rgba32 output = new RawBlock4X4Rgba32();
- var pixels = output.AsSpan;
var color0 = this.color0.ToColorRgb24();
var color1 = this.color1.ToColorRgb24();
var a0 = Alpha0;
var a1 = Alpha1;
- Span colors = stackalloc ColorRgb24[] {
+ ColorRgb24[] colors = new ColorRgb24[] {
color0,
color1,
color0 * (2.0 / 3.0) + color1 * (1.0 / 3.0),
color0 * (1.0 / 3.0) + color1 * (2.0 / 3.0)
};
- Span alphas = a0 > a1 ? stackalloc byte[] {
+ byte[] alphas = a0 > a1 ? new byte[] {
a0,
a1,
(byte)(6 / 7.0 * a0 + 1 / 7.0 * a1),
@@ -208,7 +204,7 @@ public readonly RawBlock4X4Rgba32 Decode()
(byte)(3 / 7.0 * a0 + 4 / 7.0 * a1),
(byte)(2 / 7.0 * a0 + 5 / 7.0 * a1),
(byte)(1 / 7.0 * a0 + 6 / 7.0 * a1),
- } : stackalloc byte[] {
+ } : new byte[] {
a0,
a1,
(byte)(4 / 5.0 * a0 + 1 / 5.0 * a1),
@@ -219,12 +215,12 @@ public readonly RawBlock4X4Rgba32 Decode()
255
};
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
int colorIndex = (int)((colorIndices >> (i * 2)) & 0b11);
var color = colors[colorIndex];
- pixels[i] = new Rgba32(color.r, color.g, color.b, alphas[GetAlphaIndex(i)]);
+ output[i] = new Rgba32(color.r, color.g, color.b, alphas[GetAlphaIndex(i)]);
}
return output;
}
@@ -237,7 +233,7 @@ internal unsafe struct Bc4Block
public byte Red0
{
- readonly get => (byte)(redBlock & 0xFFUL);
+ get => (byte)(redBlock & 0xFFUL);
set
{
redBlock &= ~0xFFUL;
@@ -247,7 +243,7 @@ public byte Red0
public byte Red1
{
- readonly get => (byte)((redBlock >> 8) & 0xFFUL);
+ get => (byte)((redBlock >> 8) & 0xFFUL);
set
{
redBlock &= ~0xFF00UL;
@@ -255,7 +251,7 @@ public byte Red1
}
}
- public readonly byte GetRedIndex(int pixelIndex)
+ public byte GetRedIndex(int pixelIndex)
{
ulong mask = 0b0111UL << (pixelIndex * 3 + 16);
int shift = (pixelIndex * 3 + 16);
@@ -271,15 +267,14 @@ public void SetRedIndex(int pixelIndex, byte redIndex)
redBlock |= (ulong)(redIndex & 0b111) << shift;
}
- public readonly RawBlock4X4Rgba32 Decode(bool redAsLuminance)
+ public RawBlock4X4Rgba32 Decode(bool redAsLuminance)
{
RawBlock4X4Rgba32 output = new RawBlock4X4Rgba32();
- var pixels = output.AsSpan;
var r0 = Red0;
var r1 = Red1;
- Span reds = r0 > r1 ? stackalloc byte[] {
+ byte[] reds = r0 > r1 ? new byte[] {
r0,
r1,
(byte)(6 / 7.0 * r0 + 1 / 7.0 * r1),
@@ -288,7 +283,7 @@ public readonly RawBlock4X4Rgba32 Decode(bool redAsLuminance)
(byte)(3 / 7.0 * r0 + 4 / 7.0 * r1),
(byte)(2 / 7.0 * r0 + 5 / 7.0 * r1),
(byte)(1 / 7.0 * r0 + 6 / 7.0 * r1),
- } : stackalloc byte[] {
+ } : new byte[] {
r0,
r1,
(byte)(4 / 5.0 * r0 + 1 / 5.0 * r1),
@@ -301,18 +296,18 @@ public readonly RawBlock4X4Rgba32 Decode(bool redAsLuminance)
if (redAsLuminance)
{
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
var index = GetRedIndex(i);
- pixels[i] = new Rgba32(reds[index], reds[index], reds[index], 255);
+ output[i] = new Rgba32(reds[index], reds[index], reds[index], 255);
}
}
else
{
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
var index = GetRedIndex(i);
- pixels[i] = new Rgba32(reds[index], 0, 0, 255);
+ output[i] = new Rgba32(reds[index], 0, 0, 255);
}
}
@@ -328,7 +323,7 @@ internal unsafe struct Bc5Block
public byte Red0
{
- readonly get => (byte)(redBlock & 0xFFUL);
+ get => (byte)(redBlock & 0xFFUL);
set
{
redBlock &= ~0xFFUL;
@@ -338,7 +333,7 @@ public byte Red0
public byte Red1
{
- readonly get => (byte)((redBlock >> 8) & 0xFFUL);
+ get => (byte)((redBlock >> 8) & 0xFFUL);
set
{
redBlock &= ~0xFF00UL;
@@ -348,7 +343,7 @@ public byte Red1
public byte Green0
{
- readonly get => (byte)(greenBlock & 0xFFUL);
+ get => (byte)(greenBlock & 0xFFUL);
set
{
greenBlock &= ~0xFFUL;
@@ -358,7 +353,7 @@ public byte Green0
public byte Green1
{
- readonly get => (byte)((greenBlock >> 8) & 0xFFUL);
+ get => (byte)((greenBlock >> 8) & 0xFFUL);
set
{
greenBlock &= ~0xFF00UL;
@@ -366,7 +361,7 @@ public byte Green1
}
}
- public readonly byte GetRedIndex(int pixelIndex)
+ public byte GetRedIndex(int pixelIndex)
{
ulong mask = 0b0111UL << (pixelIndex * 3 + 16);
int shift = (pixelIndex * 3 + 16);
@@ -382,7 +377,7 @@ public void SetRedIndex(int pixelIndex, byte redIndex)
redBlock |= (ulong)(redIndex & 0b111) << shift;
}
- public readonly byte GetGreenIndex(int pixelIndex)
+ public byte GetGreenIndex(int pixelIndex)
{
ulong mask = 0b0111UL << (pixelIndex * 3 + 16);
int shift = (pixelIndex * 3 + 16);
@@ -398,15 +393,14 @@ public void SetGreenIndex(int pixelIndex, byte greenIndex)
greenBlock |= (ulong)(greenIndex & 0b111) << shift;
}
- public readonly RawBlock4X4Rgba32 Decode()
+ public RawBlock4X4Rgba32 Decode()
{
RawBlock4X4Rgba32 output = new RawBlock4X4Rgba32();
- var pixels = output.AsSpan;
var r0 = Red0;
var r1 = Red1;
- Span reds = r0 > r1 ? stackalloc byte[] {
+ byte[] reds = r0 > r1 ? new byte[] {
r0,
r1,
(byte)(6 / 7.0 * r0 + 1 / 7.0 * r1),
@@ -415,7 +409,7 @@ public readonly RawBlock4X4Rgba32 Decode()
(byte)(3 / 7.0 * r0 + 4 / 7.0 * r1),
(byte)(2 / 7.0 * r0 + 5 / 7.0 * r1),
(byte)(1 / 7.0 * r0 + 6 / 7.0 * r1),
- } : stackalloc byte[] {
+ } : new byte[] {
r0,
r1,
(byte)(4 / 5.0 * r0 + 1 / 5.0 * r1),
@@ -429,7 +423,7 @@ public readonly RawBlock4X4Rgba32 Decode()
var g0 = Green0;
var g1 = Green1;
- Span greens = g0 > g1 ? stackalloc byte[] {
+ byte[] greens = g0 > g1 ? new byte[] {
g0,
g1,
(byte)(6 / 7.0 * g0 + 1 / 7.0 * g1),
@@ -438,7 +432,7 @@ public readonly RawBlock4X4Rgba32 Decode()
(byte)(3 / 7.0 * g0 + 4 / 7.0 * g1),
(byte)(2 / 7.0 * g0 + 5 / 7.0 * g1),
(byte)(1 / 7.0 * g0 + 6 / 7.0 * g1),
- } : stackalloc byte[] {
+ } : new byte[] {
g0,
g1,
(byte)(4 / 5.0 * g0 + 1 / 5.0 * g1),
@@ -449,11 +443,11 @@ public readonly RawBlock4X4Rgba32 Decode()
255
};
- for (int i = 0; i < pixels.Length; i++)
+ for (int i = 0; i < 16; i++)
{
var redIndex = GetRedIndex(i);
var greenIndex = GetGreenIndex(i);
- pixels[i] = new Rgba32(reds[redIndex], greens[greenIndex], 0, 255);
+ output[i] = new Rgba32(reds[redIndex], greens[greenIndex], 0, 255);
}
return output;
diff --git a/BCnEnc.Net/Shared/ImageToBlocks.cs b/BCnEnc.Net/Shared/ImageToBlocks.cs
index fb8b7bb..50c62f5 100644
--- a/BCnEnc.Net/Shared/ImageToBlocks.cs
+++ b/BCnEnc.Net/Shared/ImageToBlocks.cs
@@ -1,30 +1,28 @@
using System;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
internal static class ImageToBlocks
{
- internal static RawBlock4X4Rgba32[] ImageTo4X4(ImageFrame image, out int blocksWidth, out int blocksHeight)
+ internal static RawBlock4X4Rgba32[] ImageTo4X4(byte[] imageRgba, int imageWidth, int imageHeight, out int blocksWidth, out int blocksHeight)
{
- blocksWidth = (int)MathF.Ceiling(image.Width / 4.0f);
- blocksHeight = (int)MathF.Ceiling(image.Height / 4.0f);
+ blocksWidth = (int)Math.Ceiling(imageWidth / 4.0f);
+ blocksHeight = (int)Math.Ceiling(imageHeight / 4.0f);
RawBlock4X4Rgba32[] output = new RawBlock4X4Rgba32[blocksWidth * blocksHeight];
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- for (int y = 0; y < image.Height; y++)
+ for (int y = 0; y < imageHeight; y++)
{
- for (int x = 0; x < image.Width; x++)
+ for (int x = 0; x < imageWidth; x++)
{
- Rgba32 color = pixels[x + y * image.Width];
- int blockIndexX = (int)MathF.Floor(x / 4.0f);
- int blockIndexY = (int)MathF.Floor(y / 4.0f);
+ byte r = imageRgba[(x + y * imageWidth) * 4 + 0];
+ byte g = imageRgba[(x + y * imageWidth) * 4 + 1];
+ byte b = imageRgba[(x + y * imageWidth) * 4 + 2];
+ byte a = imageRgba[(x + y * imageWidth) * 4 + 3];
+ Rgba32 color = new Rgba32(r, g, b, a);
+ int blockIndexX = (int)Math.Floor(x / 4.0f);
+ int blockIndexY = (int)Math.Floor(y / 4.0f);
int blockInternalIndexX = x % 4;
int blockInternalIndexY = y % 4;
@@ -33,9 +31,9 @@ internal static RawBlock4X4Rgba32[] ImageTo4X4(ImageFrame image, out int
}
//Fill in block y with edge color
- if (image.Height % 4 != 0)
+ if (imageHeight % 4 != 0)
{
- int yPaddingStart = image.Height % 4;
+ int yPaddingStart = imageHeight % 4;
for (int i = 0; i < blocksWidth; i++)
{
var lastBlock = output[i + blocksWidth * (blocksHeight - 1)];
@@ -51,9 +49,9 @@ internal static RawBlock4X4Rgba32[] ImageTo4X4(ImageFrame image, out int
}
//Fill in block x with edge color
- if (image.Width % 4 != 0)
+ if (imageWidth % 4 != 0)
{
- int xPaddingStart = image.Width % 4;
+ int xPaddingStart = imageWidth % 4;
for (int i = 0; i < blocksHeight; i++)
{
var lastBlock = output[blocksWidth - 1 + i * blocksWidth];
@@ -71,60 +69,55 @@ internal static RawBlock4X4Rgba32[] ImageTo4X4(ImageFrame image, out int
return output;
}
- internal static Image ImageFromRawBlocks(RawBlock4X4Rgba32[,] blocks, int blocksWidth, int blocksHeight)
+ internal static byte[] ImageFromRawBlocks(RawBlock4X4Rgba32[,] blocks, int blocksWidth, int blocksHeight)
=> ImageFromRawBlocks(blocks, blocksWidth, blocksHeight, blocksWidth * 4, blocksHeight * 4);
- internal static Image ImageFromRawBlocks(RawBlock4X4Rgba32[,] blocks, int blocksWidth, int blocksHeight, int pixelWidth, int pixelHeight)
+ internal static byte[] ImageFromRawBlocks(RawBlock4X4Rgba32[,] blocks, int blocksWidth, int blocksHeight, int pixelWidth, int pixelHeight)
{
- Image output = new Image(pixelWidth, pixelHeight);
-
- if (!output.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
+ byte[] output = new byte[pixelWidth * pixelHeight * 4];
- for (int y = 0; y < output.Height; y++)
+ for (int y = 0; y < pixelHeight; y++)
{
- for (int x = 0; x < output.Width; x++)
+ for (int x = 0; x < pixelWidth; x++)
{
- int blockIndexX = (int)MathF.Floor(x / 4.0f);
- int blockIndexY = (int)MathF.Floor(y / 4.0f);
+ int blockIndexX = (int)Math.Floor(x / 4.0f);
+ int blockIndexY = (int)Math.Floor(y / 4.0f);
int blockInternalIndexX = x % 4;
int blockInternalIndexY = y % 4;
- pixels[x + y * output.Width] =
- blocks[blockIndexX, blockIndexY]
- [blockInternalIndexX, blockInternalIndexY];
+ output[(x + y * pixelWidth) * 4 + 0] = blocks[blockIndexX, blockIndexY][blockInternalIndexX, blockInternalIndexY].R;
+ output[(x + y * pixelWidth) * 4 + 1] = blocks[blockIndexX, blockIndexY][blockInternalIndexX, blockInternalIndexY].G;
+ output[(x + y * pixelWidth) * 4 + 2] = blocks[blockIndexX, blockIndexY][blockInternalIndexX, blockInternalIndexY].B;
+ output[(x + y * pixelWidth) * 4 + 3] = blocks[blockIndexX, blockIndexY][blockInternalIndexX, blockInternalIndexY].A;
}
}
return output;
}
- internal static Image ImageFromRawBlocks(RawBlock4X4Rgba32[] blocks, int blocksWidth, int blocksHeight)
+ internal static byte[] ImageFromRawBlocks(RawBlock4X4Rgba32[] blocks, int blocksWidth, int blocksHeight)
=> ImageFromRawBlocks(blocks, blocksWidth, blocksHeight, blocksWidth * 4, blocksHeight * 4);
- internal static Image ImageFromRawBlocks(RawBlock4X4Rgba32[] blocks, int blocksWidth, int blocksHeight, int pixelWidth, int pixelHeight)
+ internal static byte[] ImageFromRawBlocks(RawBlock4X4Rgba32[] blocks, int blocksWidth, int blocksHeight, int pixelWidth, int pixelHeight)
{
- Image output = new Image(pixelWidth, pixelHeight);
-
- if (!output.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
+ byte[] output = new byte[pixelWidth * pixelHeight * 4];
- for (int y = 0; y < output.Height; y++)
+
+ for (int y = 0; y < pixelHeight; y++)
{
- for (int x = 0; x < output.Width; x++)
+ for (int x = 0; x < pixelWidth; x++)
{
- int blockIndexX = (int)MathF.Floor(x / 4.0f);
- int blockIndexY = (int)MathF.Floor(y / 4.0f);
+ int blockIndexX = (int)Math.Floor(x / 4.0f);
+ int blockIndexY = (int)Math.Floor(y / 4.0f);
int blockInternalIndexX = x % 4;
int blockInternalIndexY = y % 4;
-
- pixels[x + y * output.Width] =
- blocks[blockIndexX + blockIndexY * blocksWidth]
- [blockInternalIndexX, blockInternalIndexY];
+
+ output[(x + y * pixelWidth) * 4 + 0] = blocks[blockIndexX + blockIndexY * blocksWidth][blockInternalIndexX, blockInternalIndexY].R;
+ output[(x + y * pixelWidth) * 4 + 1] = blocks[blockIndexX + blockIndexY * blocksWidth][blockInternalIndexX, blockInternalIndexY].G;
+ output[(x + y * pixelWidth) * 4 + 2] = blocks[blockIndexX + blockIndexY * blocksWidth][blockInternalIndexX, blockInternalIndexY].B;
+ output[(x + y * pixelWidth) * 4 + 3] = blocks[blockIndexX + blockIndexY * blocksWidth][blockInternalIndexX, blockInternalIndexY].A;
}
}
diff --git a/BCnEnc.Net/Shared/KtxFile.cs b/BCnEnc.Net/Shared/KtxFile.cs
index 27ce05f..276be55 100644
--- a/BCnEnc.Net/Shared/KtxFile.cs
+++ b/BCnEnc.Net/Shared/KtxFile.cs
@@ -217,7 +217,7 @@ public uint GetSizeWithPadding()
public static KtxKeyValuePair ReadKeyValuePair(BinaryReader br, out int bytesRead)
{
uint totalSize = br.ReadUInt32();
- Span keyValueBytes = stackalloc byte[(int)totalSize];
+ byte[] keyValueBytes = new byte[(int)totalSize];
br.Read(keyValueBytes);
// Find the key's null terminator
@@ -237,12 +237,12 @@ public static KtxKeyValuePair ReadKeyValuePair(BinaryReader br, out int bytesRea
int keySize = i;
- string key = UTF8.GetString(keyValueBytes.Slice(0, keySize));
+ string key = UTF8.GetString(keyValueBytes, 0, keySize);
int valueSize = (int)(totalSize - keySize - 1);
- Span valueBytes = keyValueBytes.Slice(i + 1, valueSize);
+ byte[] valueBytes = keyValueBytes.Slice(i + 1, valueSize);
byte[] value = new byte[valueSize];
- valueBytes.CopyTo(value);
+ valueBytes.CopyTo(value, 0);
int paddingBytes = (int)(3 - ((totalSize + 3) % 4));
br.SkipPadding(paddingBytes);
@@ -254,8 +254,8 @@ public static KtxKeyValuePair ReadKeyValuePair(BinaryReader br, out int bytesRea
public static uint WriteKeyValuePair(BinaryWriter bw, KtxKeyValuePair pair)
{
int keySpanLength = UTF8.GetByteCount(pair.Key);
- Span keySpan = stackalloc byte[keySpanLength];
- Span valueSpan = pair.Value;
+ byte[] keySpan = new byte[keySpanLength];
+ byte[] valueSpan = pair.Value;
uint totalSize = (uint)(keySpan.Length + 1 + valueSpan.Length);
int paddingBytes = (int)(3 - ((totalSize + 3) % 4));
@@ -290,7 +290,7 @@ public unsafe struct KtxHeader
public bool VerifyHeader()
{
- Span id = stackalloc byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+ byte[] id = new byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
for (int i = 0; i < id.Length; i++)
{
if (Identifier[i] != id[i]) return false;
@@ -301,7 +301,7 @@ public bool VerifyHeader()
public static KtxHeader InitializeCompressed(int width, int height, GlInternalFormat internalFormat, GLFormat baseInternalFormat)
{
KtxHeader header = new KtxHeader();
- Span id = stackalloc byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+ byte[] id = new byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
for (int i = 0; i < id.Length; i++)
{
header.Identifier[i] = id[i];
@@ -321,7 +321,7 @@ public static KtxHeader InitializeCompressed(int width, int height, GlInternalFo
public static KtxHeader InitializeUncompressed(int width, int height, GLType type, GLFormat format, uint glTypeSize, GlInternalFormat internalFormat, GLFormat baseInternalFormat)
{
KtxHeader header = new KtxHeader();
- Span id = stackalloc byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
+ byte[] id = new byte[] { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
for (int i = 0; i < id.Length; i++)
{
header.Identifier[i] = id[i];
diff --git a/BCnEnc.Net/Shared/LinearClustering.cs b/BCnEnc.Net/Shared/LinearClustering.cs
index 5df2b97..f41d2db 100644
--- a/BCnEnc.Net/Shared/LinearClustering.cs
+++ b/BCnEnc.Net/Shared/LinearClustering.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
@@ -63,27 +62,27 @@ public ClusterCenter(LabXy labxy)
count = 0;
}
- public readonly float Distance(LabXy other, float m, float s)
+ public float Distance(LabXy other, float m, float s)
{
- float dLab = MathF.Sqrt(
- MathF.Pow(l - other.l, 2) +
- MathF.Pow(a - other.a, 2) +
- MathF.Pow(b - other.b, 2));
-
- float dXy = MathF.Sqrt(
- MathF.Pow(x - other.x, 2) +
- MathF.Pow(y - other.y, 2));
+ float dLab = (float)Math.Sqrt(
+ (float)Math.Pow(l - other.l, 2) +
+ (float)Math.Pow(a - other.a, 2) +
+ (float)Math.Pow(b - other.b, 2));
+
+ float dXy = (float)Math.Sqrt(
+ (float)Math.Pow(x - other.x, 2) +
+ (float)Math.Pow(y - other.y, 2));
return dLab + (m / s) * dXy;
}
- public readonly float Distance(ClusterCenter other, float m, float s)
+ public float Distance(ClusterCenter other, float m, float s)
{
- float dLab = MathF.Sqrt(
+ float dLab = (float)Math.Sqrt(
(l - other.l) * (l - other.l) +
(a - other.a) * (a - other.a) +
(b - other.b) * (b - other.b));
- float dXy = MathF.Sqrt(
+ float dXy = (float)Math.Sqrt(
(x - other.x) * (x - other.x) +
(y - other.y) * (y - other.y));
return dLab + m / s * dXy;
@@ -121,7 +120,7 @@ public readonly float Distance(ClusterCenter other, float m, float s)
/// the more spatial proximity is emphasized and the more compact the cluster,
/// M should be in range of 1 to 20.
///
- public static int[] ClusterPixels(ReadOnlySpan pixels, int width, int height,
+ public static int[] ClusterPixels(Rgba32[] pixels, int width, int height,
int clusters, float m = 10, int maxIterations = 10, bool enforceConnectivity = true)
{
@@ -131,14 +130,13 @@ public static int[] ClusterPixels(ReadOnlySpan pixels, int width, int he
}
//Grid interval S
- float S = MathF.Sqrt(pixels.Length / (float)clusters);
+ float S = (float)Math.Sqrt(pixels.Length / (float)clusters);
int[] clusterIndices = new int[pixels.Length];
var labXys = ConvertToLabXy(pixels, width, height);
-
- Span clusterCenters = InitialClusterCenters(width, height, clusters, S, labXys);
- Span previousCenters = new ClusterCenter[clusters];
+ ClusterCenter[] clusterCenters = InitialClusterCenters(width, height, clusters, S, labXys);
+ ClusterCenter[] previousCenters = new ClusterCenter[clusters];
float Error = 999;
const float threshold = 0.1f;
@@ -151,9 +149,9 @@ public static int[] ClusterPixels(ReadOnlySpan pixels, int width, int he
}
iter++;
- clusterCenters.CopyTo(previousCenters);
+ clusterCenters.CopyTo(previousCenters, 0);
- Array.Fill(clusterIndices, -1);
+ clusterIndices.Fill(-1);
// Find closest cluster for pixels
for (int j = 0; j < clusters; j++)
@@ -189,15 +187,16 @@ public static int[] ClusterPixels(ReadOnlySpan pixels, int width, int he
Error = RecalculateCenters(clusters, m, labXys, clusterIndices, previousCenters, S, ref clusterCenters);
}
- if (enforceConnectivity) {
+ if (enforceConnectivity)
+ {
clusterIndices = EnforceConnectivity(clusterIndices, width, height, clusters);
}
-
+
return clusterIndices;
}
private static float RecalculateCenters(int clusters, float m, LabXy[] labXys, int[] clusterIndices,
- Span previousCenters, float S, ref Span clusterCenters)
+ ClusterCenter[] previousCenters, float S, ref ClusterCenter[] clusterCenters)
{
clusterCenters.Clear();
for (int i = 0; i < labXys.Length; i++)
@@ -209,9 +208,11 @@ private static float RecalculateCenters(int clusters, float m, LabXy[] labXys, i
{
int bestCluster = 0;
float bestDistance = previousCenters[0].Distance(labXys[i], m, S);
- for (int j = 1; j < clusters; j++) {
+ for (int j = 1; j < clusters; j++)
+ {
float dist = previousCenters[j].Distance(labXys[i], m, S);
- if (dist < bestDistance) {
+ if (dist < bestDistance)
+ {
bestDistance = dist;
bestCluster = j;
}
@@ -219,7 +220,8 @@ private static float RecalculateCenters(int clusters, float m, LabXy[] labXys, i
clusterCenters[bestCluster] += labXys[i];
clusterIndices[i] = bestCluster;
}
- else {
+ else
+ {
clusterCenters[clusterIndex] += labXys[i];
}
}
@@ -242,36 +244,39 @@ private static ClusterCenter[] InitialClusterCenters(int width, int height, int
{
ClusterCenter[] clusterCenters = new ClusterCenter[clusters];
- if (clusters == 2) {
- int x0 = (int)MathF.Floor(width * 0.333f);
- int y0 = (int)MathF.Floor(height * 0.333f);
+ if (clusters == 2)
+ {
+ int x0 = (int)(float)Math.Floor(width * 0.333f);
+ int y0 = (int)(float)Math.Floor(height * 0.333f);
- int x1 = (int)MathF.Floor(width * 0.666f);
- int y1 = (int)MathF.Floor(height * 0.666f);
+ int x1 = (int)(float)Math.Floor(width * 0.666f);
+ int y1 = (int)(float)Math.Floor(height * 0.666f);
int i0 = x0 + y0 * width;
clusterCenters[0] = new ClusterCenter(labXys[i0]);
int i1 = x1 + y1 * width;
clusterCenters[1] = new ClusterCenter(labXys[i1]);
- }else if(clusters == 3)
+ }
+ else if (clusters == 3)
{
- int x0 = (int)MathF.Floor(width * 0.333f);
- int y0 = (int)MathF.Floor(height * 0.333f);
+ int x0 = (int)(float)Math.Floor(width * 0.333f);
+ int y0 = (int)(float)Math.Floor(height * 0.333f);
int i0 = x0 + y0 * width;
clusterCenters[0] = new ClusterCenter(labXys[i0]);
- int x1 = (int)MathF.Floor(width * 0.666f);
- int y1 = (int)MathF.Floor(height * 0.333f);
+ int x1 = (int)(float)Math.Floor(width * 0.666f);
+ int y1 = (int)(float)Math.Floor(height * 0.333f);
int i1 = x1 + y1 * width;
clusterCenters[1] = new ClusterCenter(labXys[i1]);
- int x2 = (int)MathF.Floor(width * 0.5f);
- int y2 = (int)MathF.Floor(height * 0.666f);
+ int x2 = (int)(float)Math.Floor(width * 0.5f);
+ int y2 = (int)(float)Math.Floor(height * 0.666f);
int i2 = x2 + y2 * width;
clusterCenters[2] = new ClusterCenter(labXys[i2]);
}
- else {
+ else
+ {
int cIdx = 0;
//Choose initial centers
for (float x = S / 2; x < width; x += S)
@@ -292,7 +297,7 @@ private static ClusterCenter[] InitialClusterCenters(int width, int height, int
return clusterCenters;
}
- private static LabXy[] ConvertToLabXy(ReadOnlySpan pixels, int width, int height)
+ private static LabXy[] ConvertToLabXy(Rgba32[] pixels, int width, int height)
{
LabXy[] labXys = new LabXy[pixels.Length];
//Convert pixels to LabXy
@@ -318,9 +323,9 @@ private static LabXy[] ConvertToLabXy(ReadOnlySpan pixels, int width, in
private static int[] EnforceConnectivity(int[] oldLabels, int width, int height, int clusters)
{
- ReadOnlySpan neighborX = new[] { -1, 0, 1, 0 };
- ReadOnlySpan neighborY = new[] { 0, -1, 0, 1 };
-
+ int[] neighborX = new[] { -1, 0, 1, 0 };
+ int[] neighborY = new[] { 0, -1, 0, 1 };
+
int sSquared = (width * height) / clusters;
List clusterX = new List(sSquared);
@@ -329,14 +334,15 @@ private static int[] EnforceConnectivity(int[] oldLabels, int width, int height,
int adjacentLabel = 0;
int[] newLabels = new int[oldLabels.Length];
bool[] usedLabels = new bool[clusters];
- Array.Fill(newLabels, -1);
+ newLabels.Fill(-1);
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
int xyIndex = x + y * width;
- if (newLabels[xyIndex] < 0) {
+ if (newLabels[xyIndex] < 0)
+ {
int label = oldLabels[xyIndex];
newLabels[xyIndex] = label;
@@ -389,7 +395,8 @@ private static int[] EnforceConnectivity(int[] oldLabels, int width, int height,
newLabels[(clusterY[i] * width + clusterX[i])] = adjacentLabel;
}
}
- else {
+ else
+ {
usedLabels[label] = true;
}
diff --git a/BCnEnc.Net/Shared/MipMapper.cs b/BCnEnc.Net/Shared/MipMapper.cs
deleted file mode 100644
index 23329e2..0000000
--- a/BCnEnc.Net/Shared/MipMapper.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.Collections.Generic;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
-
-namespace BCnEncoder.Shared
-{
- public static class MipMapper
- {
-
- public static uint CalculateMipChainLength(int width, int height, uint maxNumMipMaps) {
- if (maxNumMipMaps == 1) {
- return 1;
- }
- if (maxNumMipMaps == 0) {
- maxNumMipMaps = 999;
- }
- uint output = 0;
- for (uint mipLevel = 1; mipLevel < maxNumMipMaps; mipLevel++) {
- int mipWidth = Math.Max(1, width / (int)(Math.Pow(2, mipLevel)));
- int mipHeight = Math.Max(1, height / (int)(Math.Pow(2, mipLevel)));
- if (mipWidth == 1 && mipHeight == 1) {
- output = mipLevel + 1;
- break;
- }
- }
- return output;
- }
-
- public static List> GenerateMipChain(Image sourceImage, ref uint numMipMaps) {
- List> result = new List>();
- result.Add(sourceImage.Clone());
-
- if (numMipMaps == 1) {
- return result;
- }
-
- if (numMipMaps == 0) {
- numMipMaps = 999;
- }
-
- for (uint mipLevel = 1; mipLevel < numMipMaps; mipLevel++) {
- int mipWidth = Math.Max(1, sourceImage.Width / (int)(Math.Pow(2, mipLevel)));
- int mipHeight = Math.Max(1, sourceImage.Height / (int)(Math.Pow(2, mipLevel)));
-
- var newImage = sourceImage.Clone(x => x.Resize(mipWidth, mipHeight));
- result.Add(newImage);
-
- if (mipWidth == 1 && mipHeight == 1) {
- numMipMaps = mipLevel + 1;
- break;
- }
- }
-
- return result;
- }
- }
-}
diff --git a/BCnEnc.Net/Shared/PcaVectors.cs b/BCnEnc.Net/Shared/PcaVectors.cs
index 999e5c8..acac61e 100644
--- a/BCnEnc.Net/Shared/PcaVectors.cs
+++ b/BCnEnc.Net/Shared/PcaVectors.cs
@@ -1,5 +1,4 @@
using System;
-using SixLabors.ImageSharp.PixelFormats;
using Vector3 = System.Numerics.Vector3;
using Vector4 = System.Numerics.Vector4;
using Matrix4x4 = System.Numerics.Matrix4x4;
@@ -11,7 +10,7 @@ internal static class PcaVectors
private const int c565_5_mask = 0xF8;
private const int c565_6_mask = 0xFC;
- private static void ConvertToVector4(ReadOnlySpan colors, Span vectors)
+ private static void ConvertToVector4(Rgba32[] colors, Vector4[] vectors)
{
for (int i = 0; i < colors.Length; i++)
{
@@ -22,7 +21,7 @@ private static void ConvertToVector4(ReadOnlySpan colors, Span
}
}
- private static Vector4 CalculateMean(Span colors)
+ private static Vector4 CalculateMean(Vector4[] colors)
{
float r = 0;
@@ -46,7 +45,7 @@ private static Vector4 CalculateMean(Span colors)
);
}
- internal static Matrix4x4 CalculateCovariance(Span values, out Vector4 mean) {
+ internal static Matrix4x4 CalculateCovariance(Vector4[] values, out Vector4 mean) {
mean = CalculateMean(values);
for (int i = 0; i < values.Length; i++)
{
@@ -112,9 +111,9 @@ internal static Vector4 CalculatePrincipalAxis(Matrix4x4 covarianceMatrix) {
return lastDa;
}
- public static void Create(Span colors, out Vector3 mean, out Vector3 principalAxis)
+ public static void Create(Rgba32[] colors, out Vector3 mean, out Vector3 principalAxis)
{
- Span vectors = stackalloc Vector4[colors.Length];
+ Vector4[] vectors = new Vector4[colors.Length];
ConvertToVector4(colors, vectors);
@@ -132,9 +131,9 @@ public static void Create(Span colors, out Vector3 mean, out Vector3 pri
}
- public static void CreateWithAlpha(Span colors, out Vector4 mean, out Vector4 principalAxis)
+ public static void CreateWithAlpha(Rgba32[] colors, out Vector4 mean, out Vector4 principalAxis)
{
- Span vectors = stackalloc Vector4[colors.Length];
+ Vector4[] vectors = new Vector4[colors.Length];
ConvertToVector4(colors, vectors);
var cov = CalculateCovariance(vectors, out mean);
@@ -142,7 +141,7 @@ public static void CreateWithAlpha(Span colors, out Vector4 mean, out Ve
}
- public static void GetExtremePoints(Span colors, Vector3 mean, Vector3 principalAxis, out ColorRgb24 min,
+ public static void GetExtremePoints(Rgba32[] colors, Vector3 mean, Vector3 principalAxis, out ColorRgb24 min,
out ColorRgb24 max)
{
@@ -182,7 +181,7 @@ public static void GetExtremePoints(Span colors, Vector3 mean, Vector3 p
max = new ColorRgb24((byte)maxR, (byte)maxG, (byte)maxB);
}
- public static void GetMinMaxColor565(Span colors, Vector3 mean, Vector3 principalAxis,
+ public static void GetMinMaxColor565(Rgba32[] colors, Vector3 mean, Vector3 principalAxis,
out ColorRgb565 min, out ColorRgb565 max)
{
@@ -236,7 +235,7 @@ public static void GetMinMaxColor565(Span colors, Vector3 mean, Vector3
}
- public static void GetExtremePointsWithAlpha(Span colors, Vector4 mean, Vector4 principalAxis, out Vector4 min,
+ public static void GetExtremePointsWithAlpha(Rgba32[] colors, Vector4 mean, Vector4 principalAxis, out Vector4 min,
out Vector4 max)
{
@@ -257,7 +256,7 @@ public static void GetExtremePointsWithAlpha(Span colors, Vector4 mean,
max = mean + (principalAxis * maxD);
}
- public static void GetOptimizedEndpoints565(Span colors, Vector3 mean, Vector3 principalAxis, out ColorRgb565 min, out ColorRgb565 max,
+ public static void GetOptimizedEndpoints565(Rgba32[] colors, Vector3 mean, Vector3 principalAxis, out ColorRgb565 min, out ColorRgb565 max,
float rWeight = 0.3f, float gWeight = 0.6f, float bWeight = 0.1f)
{
int length = colors.Length;
@@ -278,7 +277,7 @@ Vector3 Clamp565(Vector3 vec)
if (vec.Y > 63) vec.Y = 63;
if (vec.Z < 0) vec.Z = 0;
if (vec.Z > 31) vec.Z = 31;
- return new Vector3(MathF.Round(vec.X), MathF.Round(vec.Y), MathF.Round(vec.Z));
+ return new Vector3((float)Math.Round(vec.X), (float)Math.Round(vec.Y), (float)Math.Round(vec.Z));
}
float Distance(Vector3 v, Vector3 p)
@@ -332,8 +331,8 @@ double calculateError()
endPoint0 = mean + (principalAxis * minD);
endPoint1 = mean + (principalAxis * maxD);
- endPoint0 = new Vector3(MathF.Round(endPoint0.X * 31), MathF.Round(endPoint0.Y * 63), MathF.Round(endPoint0.Z * 31));
- endPoint1 = new Vector3(MathF.Round(endPoint1.X * 31), MathF.Round(endPoint1.Y * 63), MathF.Round(endPoint1.Z * 31));
+ endPoint0 = new Vector3((float)Math.Round(endPoint0.X * 31), (float)Math.Round(endPoint0.Y * 63), (float)Math.Round(endPoint0.Z * 31));
+ endPoint1 = new Vector3((float)Math.Round(endPoint1.X * 31), (float)Math.Round(endPoint1.Y * 63), (float)Math.Round(endPoint1.Z * 31));
endPoint0 = Clamp565(endPoint0);
endPoint1 = Clamp565(endPoint1);
diff --git a/BCnEnc.Net/Shared/RawBlocks.cs b/BCnEnc.Net/Shared/RawBlocks.cs
index 551e330..5ff181f 100644
--- a/BCnEnc.Net/Shared/RawBlocks.cs
+++ b/BCnEnc.Net/Shared/RawBlocks.cs
@@ -1,32 +1,132 @@
using System;
using System.Runtime.InteropServices;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
- internal struct RawBlock4X4Rgba32 {
+ internal struct RawBlock4X4Rgba32
+ {
public Rgba32 p00, p10, p20, p30;
public Rgba32 p01, p11, p21, p31;
public Rgba32 p02, p12, p22, p32;
public Rgba32 p03, p13, p23, p33;
- public Span AsSpan => MemoryMarshal.CreateSpan(ref p00, 16);
- public Rgba32 this[int x, int y] {
- get => AsSpan[x + y * 4];
- set => AsSpan[x + y * 4] = value;
+ public Rgba32[] AsArray
+ {
+ get
+ {
+ return new[]
+ {
+ p00, p10, p20, p30,
+ p01, p11, p21, p31,
+ p02, p12, p22, p32,
+ p03, p13, p23, p33,
+ };
+ }
+ }
+
+ public Rgba32 this[int x, int y]
+ {
+ get
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: return p00;
+ case 1: return p01;
+ case 2: return p02;
+ case 3: return p03;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: return p10;
+ case 1: return p11;
+ case 2: return p12;
+ case 3: return p13;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: return p20;
+ case 1: return p21;
+ case 2: return p22;
+ case 3: return p23;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: return p30;
+ case 1: return p31;
+ case 2: return p32;
+ case 3: return p33;
+ }
+ break;
+ }
+ return default;
+ }
+ set
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: p00 = value; break;
+ case 1: p01 = value; break;
+ case 2: p02 = value; break;
+ case 3: p03 = value; break;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: p10 = value; break;
+ case 1: p11 = value; break;
+ case 2: p12 = value; break;
+ case 3: p13 = value; break;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: p20 = value; break;
+ case 1: p21 = value; break;
+ case 2: p22 = value; break;
+ case 3: p23 = value; break;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: p30 = value; break;
+ case 1: p31 = value; break;
+ case 2: p32 = value; break;
+ case 3: p33 = value; break;
+ }
+ break;
+ }
+ }
}
- public Rgba32 this[int index] {
- get => AsSpan[index];
- set => AsSpan[index] = value;
+ public Rgba32 this[int index]
+ {
+ get => this[index % 4, (int)Math.Floor(index / 4.0)];
+ set => this[index % 4, (int)Math.Floor(index / 4.0)] = value;
}
- public int CalculateError(RawBlock4X4Rgba32 other, bool useAlpha = false) {
+ public int CalculateError(RawBlock4X4Rgba32 other, bool useAlpha = false)
+ {
float error = 0;
- var pix1 = AsSpan;
- var pix2 = other.AsSpan;
- for (int i = 0; i < pix1.Length; i++) {
+ var pix1 = AsArray;
+ var pix2 = other.AsArray;
+ for (int i = 0; i < pix1.Length; i++)
+ {
var col1 = pix1[i];
var col2 = pix2[i];
@@ -38,25 +138,28 @@ public int CalculateError(RawBlock4X4Rgba32 other, bool useAlpha = false) {
error += ge * ge;
error += be * be;
- if (useAlpha) {
+ if (useAlpha)
+ {
var ae = col1.A - col2.A;
error += ae * ae * 4;
}
}
error /= pix1.Length;
- error = MathF.Sqrt(error);
+ error = (float)Math.Sqrt(error);
return (int)error;
}
- public float CalculateYCbCrError(RawBlock4X4Rgba32 other) {
+ public float CalculateYCbCrError(RawBlock4X4Rgba32 other)
+ {
float yError = 0;
float cbError = 0;
float crError = 0;
- var pix1 = AsSpan;
- var pix2 = other.AsSpan;
- for (int i = 0; i < pix1.Length; i++) {
+ var pix1 = AsArray;
+ var pix2 = other.AsArray;
+ for (int i = 0; i < pix1.Length; i++)
+ {
var col1 = new ColorYCbCr(pix1[i]);
var col2 = new ColorYCbCr(pix2[i]);
@@ -74,14 +177,16 @@ public float CalculateYCbCrError(RawBlock4X4Rgba32 other) {
return error;
}
- public float CalculateYCbCrAlphaError(RawBlock4X4Rgba32 other, float yMultiplier = 2, float alphaMultiplier = 1) {
+ public float CalculateYCbCrAlphaError(RawBlock4X4Rgba32 other, float yMultiplier = 2, float alphaMultiplier = 1)
+ {
float yError = 0;
float cbError = 0;
float crError = 0;
float alphaError = 0;
- var pix1 = AsSpan;
- var pix2 = other.AsSpan;
- for (int i = 0; i < pix1.Length; i++) {
+ var pix1 = AsArray;
+ var pix2 = other.AsArray;
+ for (int i = 0; i < pix1.Length; i++)
+ {
var col1 = new ColorYCbCrAlpha(pix1[i]);
var col2 = new ColorYCbCrAlpha(pix2[i]);
@@ -100,19 +205,23 @@ public float CalculateYCbCrAlphaError(RawBlock4X4Rgba32 other, float yMultiplier
return error;
}
- public RawBlock4X4Ycbcr ToRawBlockYcbcr() {
+ public RawBlock4X4Ycbcr ToRawBlockYcbcr()
+ {
RawBlock4X4Ycbcr rawYcbcr = new RawBlock4X4Ycbcr();
- var pixels = AsSpan;
- var ycbcrPs = rawYcbcr.AsSpan;
- for (int i = 0; i < pixels.Length; i++) {
+ var pixels = AsArray;
+ var ycbcrPs = rawYcbcr.AsArray;
+ for (int i = 0; i < pixels.Length; i++)
+ {
ycbcrPs[i] = new ColorYCbCr(pixels[i]);
}
return rawYcbcr;
}
- public bool HasTransparentPixels() {
- var pixels = AsSpan;
- for (int i = 0; i < pixels.Length; i++) {
+ public bool HasTransparentPixels()
+ {
+ var pixels = AsArray;
+ for (int i = 0; i < pixels.Length; i++)
+ {
if (pixels[i].A < 255) return true;
}
return false;
@@ -120,25 +229,131 @@ public bool HasTransparentPixels() {
}
- internal struct RawBlock4X4Ycbcr {
+ internal struct RawBlock4X4Ycbcr
+ {
public ColorYCbCr p00, p10, p20, p30;
public ColorYCbCr p01, p11, p21, p31;
public ColorYCbCr p02, p12, p22, p32;
public ColorYCbCr p03, p13, p23, p33;
- public Span AsSpan => MemoryMarshal.CreateSpan(ref p00, 16);
- public ColorYCbCr this[int x, int y] {
- get => AsSpan[x + y * 4];
- set => AsSpan[x + y * 4] = value;
+ public ColorYCbCr[] AsArray
+ {
+ get
+ {
+ return new[]
+ {
+ p00, p10, p20, p30,
+ p01, p11, p21, p31,
+ p02, p12, p22, p32,
+ p03, p13, p23, p33,
+ };
+ }
+ }
+
+ public ColorYCbCr this[int x, int y]
+ {
+ get
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: return p00;
+ case 1: return p01;
+ case 2: return p02;
+ case 3: return p03;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: return p10;
+ case 1: return p11;
+ case 2: return p12;
+ case 3: return p13;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: return p20;
+ case 1: return p21;
+ case 2: return p22;
+ case 3: return p23;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: return p30;
+ case 1: return p31;
+ case 2: return p32;
+ case 3: return p33;
+ }
+ break;
+ }
+ return default;
+ }
+ set
+ {
+ switch (x)
+ {
+ case 0:
+ switch (y)
+ {
+ case 0: p00 = value; break;
+ case 1: p01 = value; break;
+ case 2: p02 = value; break;
+ case 3: p03 = value; break;
+ }
+ break;
+ case 1:
+ switch (y)
+ {
+ case 0: p10 = value; break;
+ case 1: p11 = value; break;
+ case 2: p12 = value; break;
+ case 3: p13 = value; break;
+ }
+ break;
+ case 2:
+ switch (y)
+ {
+ case 0: p20 = value; break;
+ case 1: p21 = value; break;
+ case 2: p22 = value; break;
+ case 3: p23 = value; break;
+ }
+ break;
+ case 3:
+ switch (y)
+ {
+ case 0: p30 = value; break;
+ case 1: p31 = value; break;
+ case 2: p32 = value; break;
+ case 3: p33 = value; break;
+ }
+ break;
+ }
+ }
+ }
+
+ public ColorYCbCr this[int index]
+ {
+ get => this[index % 4, (int)Math.Floor(index / 4.0)];
+ set => this[index % 4, (int)Math.Floor(index / 4.0)] = value;
}
- public float CalculateError(RawBlock4X4Rgba32 other) {
+ public float CalculateError(RawBlock4X4Rgba32 other)
+ {
float yError = 0;
float cbError = 0;
float crError = 0;
- var pix1 = AsSpan;
- var pix2 = other.AsSpan;
- for (int i = 0; i < pix1.Length; i++) {
+ var pix1 = AsArray;
+ var pix2 = other.AsArray;
+ for (int i = 0; i < pix1.Length; i++)
+ {
var col1 = pix1[i];
var col2 = new ColorYCbCr(pix2[i]);
diff --git a/BCnEnc.Net/Shared/RgbBoundingBox.cs b/BCnEnc.Net/Shared/RgbBoundingBox.cs
index 3e72628..cd2b99c 100644
--- a/BCnEnc.Net/Shared/RgbBoundingBox.cs
+++ b/BCnEnc.Net/Shared/RgbBoundingBox.cs
@@ -1,5 +1,4 @@
using System;
-using SixLabors.ImageSharp.PixelFormats;
namespace BCnEncoder.Shared
{
@@ -11,7 +10,7 @@ namespace BCnEncoder.Shared
internal static class RgbBoundingBox
{
- public static void Create565(ReadOnlySpan colors, out ColorRgb565 min, out ColorRgb565 max)
+ public static void Create565(Rgba32[] colors, out ColorRgb565 min, out ColorRgb565 max)
{
const int colorInsetShift = 4;
const int c565_5_mask = 0xF8;
@@ -71,7 +70,7 @@ public static void Create565(ReadOnlySpan colors, out ColorRgb565 min, o
max = new ColorRgb565((byte)maxR, (byte)maxG, (byte)maxB);
}
- public static void Create565AlphaCutoff(ReadOnlySpan colors, out ColorRgb565 min, out ColorRgb565 max, int alphaCutoff = 128)
+ public static void Create565AlphaCutoff(Rgba32[] colors, out ColorRgb565 min, out ColorRgb565 max, int alphaCutoff = 128)
{
const int colorInsetShift = 4;
const int c565_5_mask = 0xF8;
@@ -129,7 +128,7 @@ public static void Create565AlphaCutoff(ReadOnlySpan colors, out ColorRg
max = new ColorRgb565((byte)maxR, (byte)maxG, (byte)maxB);
}
- public static void Create565a(ReadOnlySpan colors, out ColorRgb565 min, out ColorRgb565 max, out byte minAlpha, out byte maxAlpha)
+ public static void Create565a(Rgba32[] colors, out ColorRgb565 min, out ColorRgb565 max, out byte minAlpha, out byte maxAlpha)
{
const int colorInsetShift = 4;
const int alphaInsetShift = 5;
diff --git a/BCnEnc.Net/Shared/Rgba32.cs b/BCnEnc.Net/Shared/Rgba32.cs
new file mode 100644
index 0000000..ebd3840
--- /dev/null
+++ b/BCnEnc.Net/Shared/Rgba32.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace BCnEncoder.Shared
+{
+ public struct Rgba32
+ {
+ public byte R;
+ public byte G;
+ public byte B;
+ public byte A;
+
+ public Rgba32(byte r, byte g, byte b, byte a)
+ {
+ this.R = r;
+ this.G = g;
+ this.B = b;
+ this.A = a;
+ }
+
+ public Rgba32(byte r, byte g, byte b)
+ {
+ this.R = r;
+ this.G = g;
+ this.B = b;
+ this.A = 255;
+ }
+
+ public bool Equals(Rgba32 other)
+ {
+ return R == other.R && G == other.G && B == other.B && A == other.A;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is Rgba32 other && Equals(other);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = R.GetHashCode();
+ hashCode = (hashCode * 397) ^ G.GetHashCode();
+ hashCode = (hashCode * 397) ^ B.GetHashCode();
+ hashCode = (hashCode * 397) ^ A.GetHashCode();
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(Rgba32 left, Rgba32 right)
+ {
+ return left.Equals(right);
+ }
+
+ public static bool operator !=(Rgba32 left, Rgba32 right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override string ToString()
+ {
+ return $"{nameof(R)}: {R}, {nameof(G)}: {G}, {nameof(B)}: {B}, {nameof(A)}: {A}";
+ }
+ }
+}
diff --git a/BCnEncTests/BC1Tests.cs b/BCnEncTests/BC1Tests.cs
index 77492f4..48f0801 100644
--- a/BCnEncTests/BC1Tests.cs
+++ b/BCnEncTests/BC1Tests.cs
@@ -1,6 +1,7 @@
using BCnEncoder.Shared;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
+using Rgba32 = BCnEncoder.Shared.Rgba32;
namespace BCnEncTests
{
diff --git a/BCnEncTests/BCnEncTests.csproj b/BCnEncTests/BCnEncTests.csproj
index 9770be3..0ddd93d 100644
--- a/BCnEncTests/BCnEncTests.csproj
+++ b/BCnEncTests/BCnEncTests.csproj
@@ -8,6 +8,7 @@
+
diff --git a/BCnEncTests/BlockTests.cs b/BCnEncTests/BlockTests.cs
index 2213ed6..c94dd3b 100644
--- a/BCnEncTests/BlockTests.cs
+++ b/BCnEncTests/BlockTests.cs
@@ -4,6 +4,7 @@
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
namespace BCnEncTests
{
@@ -14,7 +15,8 @@ public void CreateBlocksExact()
{
using Image testImage = new Image(16, 16);
- var blocks = ImageToBlocks.ImageTo4X4(testImage.Frames[0], out var blocksWidth, out var blocksHeight);
+ testImage.Frames[0].TryGetSinglePixelSpan(out var span);
+ var blocks = ImageToBlocks.ImageTo4X4(span.ToArray().ToBytes(), testImage.Frames[0].Width, testImage.Frames[0].Height, out var blocksWidth, out var blocksHeight);
Assert.Equal(16, blocks.Length);
Assert.Equal(4, blocksWidth);
@@ -26,7 +28,8 @@ public void CreateBlocksPadding()
{
using Image testImage = new Image(11, 15);
- var blocks = ImageToBlocks.ImageTo4X4(testImage.Frames[0], out var blocksWidth, out var blocksHeight);
+ testImage.Frames[0].TryGetSinglePixelSpan(out var span);
+ var blocks = ImageToBlocks.ImageTo4X4(span.ToArray().ToBytes(), testImage.Frames[0].Width, testImage.Frames[0].Height, out var blocksWidth, out var blocksHeight);
Assert.Equal(12, blocks.Length);
Assert.Equal(3, blocksWidth);
@@ -45,7 +48,8 @@ public void PaddingColor()
pixels[i] = new Rgba32(0, 125, 125);
}
- var blocks = ImageToBlocks.ImageTo4X4(testImage.Frames[0], out var blocksWidth, out var blocksHeight);
+ testImage.Frames[0].TryGetSinglePixelSpan(out var span);
+ var blocks = ImageToBlocks.ImageTo4X4(span.ToArray().ToBytes(), testImage.Frames[0].Width, testImage.Frames[0].Height, out var blocksWidth, out var blocksHeight);
Assert.Equal(16, blocks.Length);
Assert.Equal(4, blocksWidth);
@@ -53,8 +57,8 @@ public void PaddingColor()
for (int x = 0; x < blocksWidth; x++) {
for (int y = 0; y < blocksHeight; y++) {
- foreach (var color in blocks[x + y * blocksWidth].AsSpan) {
- Assert.Equal(new Rgba32(0, 125, 125), color);
+ foreach (var color in blocks[x + y * blocksWidth].AsArray) {
+ Assert.Equal(new BCnEncoder.Shared.Rgba32(0, 125, 125), color);
}
}
}
@@ -77,21 +81,20 @@ public void BlocksToImage()
(byte)r.Next(255));
}
- var blocks = ImageToBlocks.ImageTo4X4(testImage.Frames[0], out var blocksWidth, out var blocksHeight);
+ testImage.Frames[0].TryGetSinglePixelSpan(out var span);
+ var blocks = ImageToBlocks.ImageTo4X4(span.ToArray().ToBytes(), testImage.Frames[0].Width, testImage.Frames[0].Height, out var blocksWidth, out var blocksHeight);
Assert.Equal(16, blocks.Length);
Assert.Equal(4, blocksWidth);
Assert.Equal(4, blocksHeight);
- using var output = ImageToBlocks.ImageFromRawBlocks(blocks, blocksWidth, blocksHeight);
-
- if (!output.TryGetSinglePixelSpan(out var pixels2)) {
- throw new Exception("Cannot get pixel span.");
- }
+ var output = ImageToBlocks.ImageFromRawBlocks(blocks, blocksWidth, blocksHeight);
- Assert.Equal(pixels.Length, pixels2.Length);
- for (int i = 0; i < pixels.Length; i++) {
- Assert.Equal(pixels[i], pixels2[i]);
+ Assert.Equal(pixels.Length * 4, output.Length);
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ Rgba32 o = new Rgba32(output[i * 4 + 0], output[i * 4 + 1], output[i * 4 + 2], output[i * 4 + 3]);
+ Assert.Equal(pixels[i], o);
}
}
@@ -100,20 +103,30 @@ public void BlockError()
{
using Image testImage = new Image(16, 16);
- var blocks = ImageToBlocks.ImageTo4X4(testImage.Frames[0], out var blocksWidth, out var blocksHeight);
+ testImage.Frames[0].TryGetSinglePixelSpan(out var span);
+ var blocks = ImageToBlocks.ImageTo4X4(span.ToArray().ToBytes(), testImage.Frames[0].Width, testImage.Frames[0].Height, out var blocksWidth, out var blocksHeight);
var block1 = blocks[2 + 2 * blocksWidth];
var block2 = blocks[2 + 2 * blocksWidth];
Assert.Equal(0, block1.CalculateError(block2));
- for (int i = 0; i < block2.AsSpan.Length; i++) {
- block2.AsSpan[i].R = (byte) (block2.AsSpan[i].R + 2);
+ for (int i = 0; i < block2.AsArray.Length; i++)
+ {
+ block2[i] = new BCnEncoder.Shared.Rgba32(
+ (byte)(block1.AsArray[i].R + 2),
+ block1.AsArray[i].G,
+ block1.AsArray[i].B,
+ block1.AsArray[i].A);
}
Assert.Equal(2, block1.CalculateError(block2));
- for (int i = 0; i < block2.AsSpan.Length; i++) {
- block2.AsSpan[i].G = (byte) (block2.AsSpan[i].R + 20);
+ for (int i = 0; i < block2.AsArray.Length; i++) {
+ block2[i] = new BCnEncoder.Shared.Rgba32(
+ block2.AsArray[i].R,
+ (byte)(block2.AsArray[i].R + 20),
+ block2.AsArray[i].B,
+ block2.AsArray[i].A);
}
Assert.Equal(22, block1.CalculateError(block2));
}
diff --git a/BCnEncTests/ClusterTests.cs b/BCnEncTests/ClusterTests.cs
index 26e613c..411aa0c 100644
--- a/BCnEncTests/ClusterTests.cs
+++ b/BCnEncTests/ClusterTests.cs
@@ -8,6 +8,7 @@
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
namespace BCnEncTests
{
@@ -23,19 +24,26 @@ public void Clusterize() {
int numClusters = (testImage.Width / 32) * (testImage.Height / 32);
- var clusters = LinearClustering.ClusterPixels(pixels, testImage.Width, testImage.Height, numClusters, 10, 10);
+ var clusters = LinearClustering.ClusterPixels(pixels.ToBcn(), testImage.Width, testImage.Height, numClusters, 10, 10);
ColorYCbCr[] pixC = new ColorYCbCr[numClusters];
int[] counts = new int[numClusters];
for (int i = 0; i < pixels.Length; i++) {
- pixC[clusters[i]] += new ColorYCbCr(pixels[i]);
+ pixC[clusters[i]] += new ColorYCbCr(pixels[i].ToBcn());
counts[clusters[i]]++;
}
for (int i = 0; i < numClusters; i++) {
pixC[i] /= counts[i];
}
- for (int i = 0; i < pixels.Length; i++) {
- pixels[i] = pixC[clusters[i]].ToRgba32();
+ for (int i = 0; i < pixels.Length; i++)
+ {
+ Rgba32 rgba = new Rgba32(
+ pixC[clusters[i]].ToRgba32().R,
+ pixC[clusters[i]].ToRgba32().G,
+ pixC[clusters[i]].ToRgba32().B,
+ pixC[clusters[i]].ToRgba32().A
+ );
+ pixels[i] = rgba;
}
using var fs = File.OpenWrite("test_cluster.png");
diff --git a/BCnEncTests/DdsReadTests.cs b/BCnEncTests/DdsReadTests.cs
index 3e94ff2..49119aa 100644
--- a/BCnEncTests/DdsReadTests.cs
+++ b/BCnEncTests/DdsReadTests.cs
@@ -9,6 +9,7 @@
using SixLabors.ImageSharp.Advanced;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
namespace BCnEncTests
{
@@ -27,10 +28,12 @@ public void ReadRgba() {
Assert.Equal((uint)images[0].Width, file.Header.dwWidth);
Assert.Equal((uint)images[0].Height, file.Header.dwHeight);
- for (int i = 0; i < images.Length; i++) {
+ for (int i = 0; i < images.Length; i++)
+ {
+ using var img =
+ Image.LoadPixelData(images[i].data, images[i].Width, images[i].Height);
using FileStream outFs = File.OpenWrite($"decoding_test_dds_rgba_mip{i}.png");
- images[i].SaveAsPng(outFs);
- images[i].Dispose();
+ img.SaveAsPng(outFs);
}
}
@@ -49,9 +52,10 @@ public void ReadBc1() {
Assert.Equal((uint)images[0].Height, file.Header.dwHeight);
for (int i = 0; i < images.Length; i++) {
+ using var img =
+ Image.LoadPixelData(images[i].data, images[i].Width, images[i].Height);
using FileStream outFs = File.OpenWrite($"decoding_test_dds_bc1_mip{i}.png");
- images[i].SaveAsPng(outFs);
- images[i].Dispose();
+ img.SaveAsPng(outFs);
}
}
@@ -70,14 +74,10 @@ public void ReadBc1a() {
Assert.Equal((uint)image.Width, file.Header.dwWidth);
Assert.Equal((uint)image.Height, file.Header.dwHeight);
- if (!image.TryGetSinglePixelSpan(out var pixels)) {
- throw new Exception("Cannot get pixel span.");
- }
- Assert.Contains(pixels.ToArray(), x => x.A == 0);
-
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
using FileStream outFs = File.OpenWrite($"decoding_test_dds_bc1a.png");
- image.SaveAsPng(outFs);
- image.Dispose();
+ img.SaveAsPng(outFs);
}
[Fact]
@@ -87,9 +87,10 @@ public void ReadBc7() {
var images = decoder.DecodeAllMipMaps(fs);
for (int i = 0; i < images.Length; i++) {
+ using var img =
+ Image.LoadPixelData(images[i].data, images[i].Width, images[i].Height);
using FileStream outFs = File.OpenWrite($"decoding_test_dds_bc7_mip{i}.png");
- images[i].SaveAsPng(outFs);
- images[i].Dispose();
+ img.SaveAsPng(outFs);
}
}
@@ -101,9 +102,10 @@ public void ReadFromStream() {
var images = decoder.DecodeAllMipMaps(fs);
for (int i = 0; i < images.Length; i++) {
+ using var img =
+ Image.LoadPixelData(images[i].data, images[i].Width, images[i].Height);
using FileStream outFs = File.OpenWrite($"decoding_test_dds_stream_bc1_mip{i}.png");
- images[i].SaveAsPng(outFs);
- images[i].Dispose();
+ img.SaveAsPng(outFs);
}
}
}
diff --git a/BCnEncTests/DdsWritingTests.cs b/BCnEncTests/DdsWritingTests.cs
index 0037918..628fd32 100644
--- a/BCnEncTests/DdsWritingTests.cs
+++ b/BCnEncTests/DdsWritingTests.cs
@@ -10,20 +10,6 @@ namespace BCnEncTests
{
public class DdsWritingTests
{
- [Fact]
- public void DdsWriteRgba() {
- var image = ImageLoader.testLenna;
-
- BcEncoder encoder = new BcEncoder();
- encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
- encoder.OutputOptions.format = CompressionFormat.RGBA;
- encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
-
- using FileStream fs = File.OpenWrite("encoding_dds_rgba.dds");
- encoder.Encode(image, fs);
- fs.Close();
- }
[Fact]
public void DdsWriteBc1() {
@@ -31,12 +17,12 @@ public void DdsWriteBc1() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC1;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc1.dds");
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
@@ -46,12 +32,12 @@ public void DdsWriteBc2() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC2;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc2.dds");
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
@@ -61,12 +47,12 @@ public void DdsWriteBc3() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC3;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc3.dds");
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
@@ -76,12 +62,12 @@ public void DdsWriteBc4() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC4;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc4.dds");
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
@@ -91,12 +77,12 @@ public void DdsWriteBc5() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC5;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc5.dds");
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
@@ -106,27 +92,12 @@ public void DdsWriteBc7() {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = CompressionFormat.BC7;
encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
using FileStream fs = File.OpenWrite("encoding_dds_bc7.dds");
- encoder.Encode(image, fs);
- fs.Close();
- }
-
- [Fact]
- public void DdsWriteCubemap() {
- var images = ImageLoader.testCubemap;
-
- BcEncoder encoder = new BcEncoder();
- encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
- encoder.OutputOptions.format = CompressionFormat.BC1;
- encoder.OutputOptions.fileFormat = OutputFileFormat.Dds;
-
- using FileStream fs = File.OpenWrite("encoding_dds_cubemap_bc1.dds");
- encoder.EncodeCubeMap(images[0],images[1],images[2],images[3],images[4],images[5], fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
}
}
diff --git a/BCnEncTests/DecodingTests.cs b/BCnEncTests/DecodingTests.cs
index 3cf65e7..e262acb 100644
--- a/BCnEncTests/DecodingTests.cs
+++ b/BCnEncTests/DecodingTests.cs
@@ -6,161 +6,190 @@
using SixLabors.ImageSharp;
using Xunit;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
+
namespace BCnEncTests
{
public class DecodingTests
{
[Fact]
- public void Bc1Decode() {
+ public void Bc1Decode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc1.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc1.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc1AlphaDecode() {
+ public void Bc1AlphaDecode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc1a.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc1a.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc2Decode() {
+ public void Bc2Decode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc2.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc2.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc3Decode() {
+ public void Bc3Decode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc3.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc3.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc4Decode() {
+ public void Bc4Decode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc4_unorm.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc4.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc5Decode() {
+ public void Bc5Decode()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc5_unorm.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc5.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc7DecodeRgb() {
+ public void Bc7DecodeRgb()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc7_rgb.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc7_rgb.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc7DecodeUnorm() {
+ public void Bc7DecodeUnorm()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc7_unorm.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc7_unorm.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
[Fact]
- public void Bc7DecodeEveryBlockType() {
+ public void Bc7DecodeEveryBlockType()
+ {
using FileStream fs = File.OpenRead(@"../../../testImages/test_decompress_bc7_types.ktx");
KtxFile file = KtxFile.Load(fs);
Assert.True(file.Header.VerifyHeader());
Assert.Equal((uint)1, file.Header.NumberOfFaces);
BcDecoder decoder = new BcDecoder();
- using var image = decoder.Decode(file);
+ var image = decoder.Decode(file);
Assert.Equal((uint)image.Width, file.Header.PixelWidth);
Assert.Equal((uint)image.Height, file.Header.PixelHeight);
using FileStream outFs = File.OpenWrite("decoding_test_bc7_types.png");
- image.SaveAsPng(outFs);
+ using var img =
+ Image.LoadPixelData(image.data, image.Width, image.Height);
+ img.SaveAsPng(outFs);
}
}
}
diff --git a/BCnEncTests/EncodingTest.cs b/BCnEncTests/EncodingTest.cs
index 1cc3240..aa00e51 100644
--- a/BCnEncTests/EncodingTest.cs
+++ b/BCnEncTests/EncodingTest.cs
@@ -506,24 +506,4 @@ public void Bc7RgbaFast()
}
}
- public class CubemapTest
- {
-
- [Fact]
- public void WriteCubeMapFile()
- {
- var images = ImageLoader.testCubemap;
-
- string filename = "encoding_bc1_cubemap.ktx";
-
- BcEncoder encoder = new BcEncoder();
- encoder.OutputOptions.quality = CompressionQuality.Fast;
- encoder.OutputOptions.generateMipMaps = true;
- encoder.OutputOptions.format = CompressionFormat.BC1;
-
- using FileStream fs = File.OpenWrite(filename);
- encoder.EncodeCubeMap(images[0],images[1],images[2],images[3],images[4],images[5], fs);
- fs.Close();
- }
- }
}
diff --git a/BCnEnc.Net/Shared/ImageQuality.cs b/BCnEncTests/ImageQuality.cs
similarity index 59%
rename from BCnEnc.Net/Shared/ImageQuality.cs
rename to BCnEncTests/ImageQuality.cs
index 1def66f..cc290ca 100644
--- a/BCnEnc.Net/Shared/ImageQuality.cs
+++ b/BCnEncTests/ImageQuality.cs
@@ -1,30 +1,39 @@
using System;
-using SixLabors.ImageSharp.PixelFormats;
+using System.Collections.Generic;
+using System.Text;
+using BCnEncoder.Shared;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
-namespace BCnEncoder.Shared
+namespace BCnEncTests
{
public class ImageQuality
{
- public static float PeakSignalToNoiseRatio(ReadOnlySpan original, ReadOnlySpan other, bool countAlpha = true) {
- if (original.Length != other.Length) {
+ public static float PeakSignalToNoiseRatio(ReadOnlySpan original, ReadOnlySpan other, bool countAlpha = true)
+ {
+ if (original.Length != other.Length)
+ {
throw new ArgumentException("Both spans should be the same length");
}
float error = 0;
- for (int i = 0; i < original.Length; i++) {
- var o = new ColorYCbCr(original[i]);
- var c = new ColorYCbCr(other[i]);
+ for (int i = 0; i < original.Length; i++)
+ {
+ var o = new ColorYCbCr(original[i].ToBcn());
+ var c = new ColorYCbCr(other[i].ToBcn());
error += (o.y - c.y) * (o.y - c.y);
error += (o.cb - c.cb) * (o.cb - c.cb);
error += (o.cr - c.cr) * (o.cr - c.cr);
- if (countAlpha) {
+ if (countAlpha)
+ {
error += ((original[i].A - other[i].A) / 255.0f) * ((original[i].A - other[i].A) / 255.0f);
}
-
+
}
- if (error < float.Epsilon) {
+ if (error < float.Epsilon)
+ {
return 100;
}
- if (countAlpha) {
+ if (countAlpha)
+ {
error /= original.Length * 4;
}
else
@@ -35,24 +44,30 @@ public static float PeakSignalToNoiseRatio(ReadOnlySpan original, ReadOn
return 20 * MathF.Log10(1 / MathF.Sqrt(error));
}
- public static float PeakSignalToNoiseRatioLuminance(ReadOnlySpan original, ReadOnlySpan other, bool countAlpha = true) {
- if (original.Length != other.Length) {
+ public static float PeakSignalToNoiseRatioLuminance(ReadOnlySpan original, ReadOnlySpan other, bool countAlpha = true)
+ {
+ if (original.Length != other.Length)
+ {
throw new ArgumentException("Both spans should be the same length");
}
float error = 0;
- for (int i = 0; i < original.Length; i++) {
- var o = new ColorYCbCr(original[i]);
- var c = new ColorYCbCr(other[i]);
+ for (int i = 0; i < original.Length; i++)
+ {
+ var o = new ColorYCbCr(original[i].ToBcn());
+ var c = new ColorYCbCr(other[i].ToBcn());
error += (o.y - c.y) * (o.y - c.y);
- if (countAlpha) {
+ if (countAlpha)
+ {
error += ((original[i].A - other[i].A) / 255.0f) * ((original[i].A - other[i].A) / 255.0f);
}
-
+
}
- if (error < float.Epsilon) {
+ if (error < float.Epsilon)
+ {
return 100;
}
- if (countAlpha) {
+ if (countAlpha)
+ {
error /= original.Length * 2;
}
else
diff --git a/BCnEncTests/TestHelper.cs b/BCnEncTests/TestHelper.cs
index e99bbd2..65626a1 100644
--- a/BCnEncTests/TestHelper.cs
+++ b/BCnEncTests/TestHelper.cs
@@ -10,35 +10,91 @@
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
using Xunit.Abstractions;
+using Rgba32 = SixLabors.ImageSharp.PixelFormats.Rgba32;
namespace BCnEncTests
{
public static class TestHelper
{
+
+ public static byte[] ToBytes(this Rgba32[] rgba)
+ {
+ byte[] output = new byte[rgba.Length * 4];
+ for (int i = 0; i < rgba.Length; i++)
+ {
+ output[i * 4 + 0] = rgba[i].R;
+ output[i * 4 + 1] = rgba[i].G;
+ output[i * 4 + 2] = rgba[i].B;
+ output[i * 4 + 3] = rgba[i].A;
+ }
+ return output;
+ }
+
+ public static BCnEncoder.Shared.Rgba32 ToBcn(this Rgba32 rgba)
+ {
+ return new BCnEncoder.Shared.Rgba32(rgba.R, rgba.G, rgba.B, rgba.A);
+ }
+
+ public static BCnEncoder.Shared.Rgba32[] ToBcn(this Span rgba)
+ {
+ BCnEncoder.Shared.Rgba32[] output = new BCnEncoder.Shared.Rgba32[rgba.Length];
+ for (int i = 0; i < rgba.Length; i++)
+ {
+ output[i] = rgba[i].ToBcn();
+ }
+ return output;
+ }
+
+ public static byte[] ToBytes(this Span rgba)
+ {
+ byte[] output = new byte[rgba.Length * 4];
+ for (int i = 0; i < rgba.Length; i++)
+ {
+ output[i * 4 + 0] = rgba[i].R;
+ output[i * 4 + 1] = rgba[i].G;
+ output[i * 4 + 2] = rgba[i].B;
+ output[i * 4 + 3] = rgba[i].A;
+ }
+ return output;
+ }
+
+ public static Rgba32[] ToRgba(this byte[] rgba)
+ {
+ Rgba32[] output = new Rgba32[rgba.Length / 4];
+ for (int i = 0; i < output.Length; i++)
+ {
+ output[i] = new Rgba32
+ (
+ rgba[i * 4 + 0],
+ rgba[i * 4 + 1],
+ rgba[i * 4 + 2],
+ rgba[i * 4 + 3]
+ );
+ }
+ return output;
+ }
+
public static float DecodeCheckPSNR(string filename, Image original) {
using FileStream fs = File.OpenRead(filename);
var ktx = KtxFile.Load(fs);
var decoder = new BcDecoder();
- using var img = decoder.Decode(ktx);
+ var img = decoder.Decode(ktx);
if (!original.TryGetSinglePixelSpan(out var pixels)) {
throw new Exception("Cannot get pixel span.");
}
- if (!img.TryGetSinglePixelSpan(out var pixels2)) {
- throw new Exception("Cannot get pixel span.");
- }
- return ImageQuality.PeakSignalToNoiseRatio(pixels, pixels2, true);
+ return ImageQuality.PeakSignalToNoiseRatio(pixels, img.data.ToRgba(), true);
}
public static void ExecuteEncodingTest(Image image, CompressionFormat format, CompressionQuality quality, string filename, ITestOutputHelper output) {
BcEncoder encoder = new BcEncoder();
encoder.OutputOptions.quality = quality;
- encoder.OutputOptions.generateMipMaps = true;
encoder.OutputOptions.format = format;
using FileStream fs = File.OpenWrite(filename);
- encoder.Encode(image, fs);
+ image.TryGetSinglePixelSpan(out var span);
+ encoder.Encode(span.ToBytes(), image.Width, image.Height, fs);
fs.Close();
var psnr = TestHelper.DecodeCheckPSNR(filename, image);
output.WriteLine("RGBA PSNR: " + psnr + "db");